diff --git a/Logging/SCCoreCameraLogger.h b/Logging/SCCoreCameraLogger.h new file mode 100644 index 0000000..36944cb --- /dev/null +++ b/Logging/SCCoreCameraLogger.h @@ -0,0 +1,66 @@ +// +// SCCoreCameraLogger.h +// Snapchat +// +// Created by Chao Pang on 3/6/18. +// + +#import + +/** + * CAMERA_CREATION_DELAY event + */ +extern NSString *const kSCCameraCreationDelayEventStartTimeKey; +extern NSString *const kSCCameraCreationDelayEventStartTimeAdjustmentKey; +extern NSString *const kSCCameraCreationDelayEventEndTimeKey; +extern NSString *const kSCCameraCreationDelayEventCaptureSessionIdKey; +extern NSString *const kSCCameraCreationDelayEventFilterLensIdKey; +extern NSString *const kSCCameraCreationDelayEventNightModeDetectedKey; +extern NSString *const kSCCameraCreationDelayEventNightModeActiveKey; +extern NSString *const kSCCameraCreationDelayEventCameraApiKey; +extern NSString *const kSCCameraCreationDelayEventCameraLevelKey; +extern NSString *const kSCCameraCreationDelayEventCameraPositionKey; +extern NSString *const kSCCameraCreationDelayEventCameraOpenSourceKey; +extern NSString *const kSCCameraCreationDelayEventContentDurationKey; +extern NSString *const kSCCameraCreationDelayEventMediaTypeKey; +extern NSString *const kSCCameraCreationDelayEventStartTypeKey; +extern NSString *const kSCCameraCreationDelayEventStartSubTypeKey; +extern NSString *const kSCCameraCreationDelayEventAnalyticsVersion; + +@interface SCCoreCameraLogger : NSObject + ++ (instancetype)sharedInstance; + +/** + * CAMERA_CREATION_DELAY event + */ +- (void)logCameraCreationDelayEventStartWithCaptureSessionId:(NSString *)captureSessionId + filterLensId:(NSString *)filterLensId + underLowLightCondition:(BOOL)underLowLightCondition + isNightModeActive:(BOOL)isNightModeActive + isBackCamera:(BOOL)isBackCamera + isMainCamera:(BOOL)isMainCamera; + +- (void)logCameraCreationDelaySplitPointRecordingGestureFinished; + +- (void)logCameraCreationDelaySplitPointStillImageCaptureApi:(NSString *)api; + +- (void)logCameraCreationDelaySplitPointPreCaptureOperationRequested; + +- (void)logCameraCreationDelaySplitPointPreCaptureOperationFinishedAt:(CFTimeInterval)time; + +- (void)updatedCameraCreationDelayWithContentDuration:(CFTimeInterval)duration; + +- (void)logCameraCreationDelaySplitPointCameraCaptureContentReady; + +- (void)logCameraCreationDelaySplitPointPreviewFinishedPreparation; + +- (void)logCameraCreationDelaySplitPointPreviewDisplayedForImage:(BOOL)isImage; + +- (void)logCameraCreationDelaySplitPointPreviewAnimationComplete:(BOOL)isImage; + +- (void)logCameraCreationDelaySplitPointPreviewFirstFramePlayed:(BOOL)isImage; + +- (void)cancelCameraCreationDelayEvent; + +@end diff --git a/Logging/SCCoreCameraLogger.m b/Logging/SCCoreCameraLogger.m new file mode 100644 index 0000000..2981f65 --- /dev/null +++ b/Logging/SCCoreCameraLogger.m @@ -0,0 +1,304 @@ +// +// SCCoreCameraLogger.m +// Snapchat +// +// Created by Chao Pang on 3/6/18. +// + +#import "SCCoreCameraLogger.h" + +#import +#import +#import +#import + +static const char *kSCCoreCameraLoggerQueueLabel = "com.snapchat.core-camera-logger-queue"; + +NSString *const kSCCameraCreationDelayEventStartTimeKey = @"start_time"; +NSString *const kSCCameraCreationDelayEventStartTimeAdjustmentKey = @"start_time_adjustment"; +NSString *const kSCCameraCreationDelayEventEndTimeKey = @"end_time"; +NSString *const kSCCameraCreationDelayEventCaptureSessionIdKey = @"capture_session_id"; +NSString *const kSCCameraCreationDelayEventFilterLensIdKey = @"filter_lens_id"; +NSString *const kSCCameraCreationDelayEventNightModeDetectedKey = @"night_mode_detected"; +NSString *const kSCCameraCreationDelayEventNightModeActiveKey = @"night_mode_active"; +NSString *const kSCCameraCreationDelayEventCameraApiKey = @"camera_api"; +NSString *const kSCCameraCreationDelayEventCameraLevelKey = @"camera_level"; +NSString *const kSCCameraCreationDelayEventCameraPositionKey = @"camera_position"; +NSString *const kSCCameraCreationDelayEventCameraOpenSourceKey = @"camera_open_source"; +NSString *const kSCCameraCreationDelayEventContentDurationKey = @"content_duration"; +NSString *const kSCCameraCreationDelayEventMediaTypeKey = @"media_type"; +NSString *const kSCCameraCreationDelayEventStartTypeKey = @"start_type"; +NSString *const kSCCameraCreationDelayEventStartSubTypeKey = @"start_sub_type"; +NSString *const kSCCameraCreationDelayEventAnalyticsVersion = @"ios_v1"; + +static inline NSUInteger SCTimeToMS(CFTimeInterval time) +{ + return (NSUInteger)(time * 1000); +} + +static NSString *SCDictionaryToJSONString(NSDictionary *dictionary) +{ + NSData *dictData = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:nil]; + return [[NSString alloc] initWithData:dictData encoding:NSUTF8StringEncoding]; +} + +@implementation SCCoreCameraLogger { + SCQueuePerformer *_performer; + NSMutableDictionary *_cameraCreationDelayParameters; + NSMutableDictionary *_cameraCreationDelaySplits; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _cameraCreationDelayParameters = [NSMutableDictionary dictionary]; + _cameraCreationDelaySplits = [NSMutableDictionary dictionary]; + _performer = [[SCQueuePerformer alloc] initWithLabel:kSCCoreCameraLoggerQueueLabel + qualityOfService:QOS_CLASS_UNSPECIFIED + queueType:DISPATCH_QUEUE_SERIAL + context:SCQueuePerformerContextCoreCamera]; + } + return self; +} + ++ (instancetype)sharedInstance +{ + static SCCoreCameraLogger *sharedInstance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[SCCoreCameraLogger alloc] init]; + }); + return sharedInstance; +} + +// Camera creation delay metrics + +- (void)logCameraCreationDelayEventStartWithCaptureSessionId:(NSString *)captureSessionId + filterLensId:(NSString *)filterLensId + underLowLightCondition:(BOOL)underLowLightCondition + isNightModeActive:(BOOL)isNightModeActive + isBackCamera:(BOOL)isBackCamera + isMainCamera:(BOOL)isMainCamera +{ + CFTimeInterval startTime = CACurrentMediaTime(); + [_performer perform:^{ + [_cameraCreationDelayParameters removeAllObjects]; + [_cameraCreationDelaySplits removeAllObjects]; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] = @(startTime); + _cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey] = captureSessionId ?: @"null"; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventFilterLensIdKey] = filterLensId ?: @"null"; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] = @(underLowLightCondition); + _cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey] = @(isNightModeActive); + _cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraPositionKey] = + isBackCamera ? @"back" : @"front"; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraOpenSourceKey] = + isMainCamera ? @"main_camera" : @"reply_camera"; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTypeKey] = SCLaunchType() ?: @"null"; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartSubTypeKey] = SCLaunchSubType() ?: @"null"; + }]; +} + +- (void)logCameraCreationDelaySplitPointRecordingGestureFinished +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + CFTimeInterval endRecordingTimeOffset = + time - [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue]; + NSNumber *recordStartTimeMillis = + (NSNumber *)_cameraCreationDelaySplits[kSCCameraSubmetricsPreCaptureOperationFinished]; + if (recordStartTimeMillis) { + CFTimeInterval timeDisplacement = ([recordStartTimeMillis doubleValue] / 1000.0) - endRecordingTimeOffset; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] = @(timeDisplacement); + } + [self _addSplitPointForKey:kSCCameraSubmetricsRecordingGestureFinished atTime:time]; + }]; +} + +- (void)logCameraCreationDelaySplitPointStillImageCaptureApi:(NSString *)api +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + if (api) { + _cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraApiKey] = api; + } + [self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationRequested atTime:time]; + }]; +} + +- (void)logCameraCreationDelaySplitPointPreCaptureOperationRequested +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + [self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationRequested atTime:time]; + }]; +} + +- (void)logCameraCreationDelaySplitPointPreCaptureOperationFinishedAt:(CFTimeInterval)time +{ + [_performer perform:^{ + [self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationFinished atTime:time]; + }]; +} + +- (void)updatedCameraCreationDelayWithContentDuration:(CFTimeInterval)duration +{ + [_performer perform:^{ + _cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey] = @(SCTimeToMS(duration)); + }]; +} + +- (void)logCameraCreationDelaySplitPointCameraCaptureContentReady +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + [self _addSplitPointForKey:kSCCameraSubmetricsCameraCaptureContentReady atTime:time]; + }]; +} + +- (void)logCameraCreationDelaySplitPointPreviewFinishedPreparation +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + [self _addSplitPointForKey:kSCCameraSubmetricsCameraCaptureContentReady atTime:time]; + }]; +} + +- (void)logCameraCreationDelaySplitPointPreviewDisplayedForImage:(BOOL)isImage +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + [self _addSplitPointForKey:kSCCameraSubmetricsPreviewLayoutReady atTime:time]; + }]; +} + +- (void)logCameraCreationDelaySplitPointPreviewAnimationComplete:(BOOL)isImage +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + [self _addSplitPointForKey:kSCCameraSubmetricsPreviewAnimationFinish atTime:time]; + if (_cameraCreationDelaySplits[kSCCameraSubmetricsPreviewPlayerReady]) { + [self _completeLogCameraCreationDelayEventWithIsImage:isImage atTime:time]; + } + }]; +} + +- (void)logCameraCreationDelaySplitPointPreviewFirstFramePlayed:(BOOL)isImage +{ + CFTimeInterval time = CACurrentMediaTime(); + [_performer perform:^{ + [self _addSplitPointForKey:kSCCameraSubmetricsPreviewPlayerReady atTime:time]; + if (_cameraCreationDelaySplits[kSCCameraSubmetricsPreviewAnimationFinish]) { + [self _completeLogCameraCreationDelayEventWithIsImage:isImage atTime:time]; + } + }]; +} + +- (void)cancelCameraCreationDelayEvent +{ + [_performer perform:^{ + [_cameraCreationDelayParameters removeAllObjects]; + [_cameraCreationDelaySplits removeAllObjects]; + }]; +} + +#pragma - Private methods + +- (void)_completeLogCameraCreationDelayEventWithIsImage:(BOOL)isImage atTime:(CFTimeInterval)time +{ + SCAssertPerformer(_performer); + if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey]) { + _cameraCreationDelayParameters[kSCCameraCreationDelayEventMediaTypeKey] = isImage ? @"image" : @"video"; + _cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey] = @(time); + [self _logCameraCreationDelayBlizzardEvent]; + } + [_cameraCreationDelayParameters removeAllObjects]; + [_cameraCreationDelaySplits removeAllObjects]; +} + +- (void)_addSplitPointForKey:(NSString *)key atTime:(CFTimeInterval)time +{ + SCAssertPerformer(_performer); + if (key) { + CFTimeInterval timeOffset = + time - [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue]; + NSNumber *timeAdjustment = + _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] ?: @(0); + _cameraCreationDelaySplits[key] = @(SCTimeToMS(timeOffset + [timeAdjustment doubleValue])); + } +} + +- (void)_logCameraCreationDelayBlizzardEvent +{ + SCAssertPerformer(_performer); + SCASharedCameraMetricParams *sharedCameraMetricsParams = [[SCASharedCameraMetricParams alloc] init]; + [sharedCameraMetricsParams setAnalyticsVersion:kSCCameraCreationDelayEventAnalyticsVersion]; + NSString *mediaType = _cameraCreationDelayParameters[kSCCameraCreationDelayEventMediaTypeKey]; + if (mediaType) { + if ([mediaType isEqualToString:@"image"]) { + [sharedCameraMetricsParams setMediaType:SCAMediaType_IMAGE]; + } else if ([mediaType isEqualToString:@"video"]) { + [sharedCameraMetricsParams setMediaType:SCAMediaType_VIDEO]; + } + } + if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] && + _cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey]) { + BOOL isNightModeDetected = + [_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] boolValue]; + BOOL isNightModeActive = + [_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey] boolValue]; + if (!isNightModeDetected) { + [sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_NOT_DETECTED]; + } else if (!isNightModeActive) { + [sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_DETECTED]; + } else if (isNightModeActive) { + [sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_ENABLED]; + } + } + + [sharedCameraMetricsParams setPowerMode:[[NSProcessInfo processInfo] isLowPowerModeEnabled] + ? @"LOW_POWER_MODE_ENABLED" + : @"LOW_POWER_MODE_DISABLED"]; + [sharedCameraMetricsParams + setFilterLensId:_cameraCreationDelayParameters[kSCCameraCreationDelayEventFilterLensIdKey] ?: @"null"]; + [sharedCameraMetricsParams + setCaptureSessionId:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey] ?: @"null"]; + [sharedCameraMetricsParams + setCameraApi:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraApiKey] ?: @"null"]; + [sharedCameraMetricsParams + setCameraPosition:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraPositionKey] ?: @"null"]; + [sharedCameraMetricsParams + setCameraOpenSource:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraOpenSourceKey] ?: @"null"]; + [sharedCameraMetricsParams + setCameraLevel:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraLevelKey] ?: @"null"]; + [sharedCameraMetricsParams + setStartType:_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTypeKey] ?: @"null"]; + [sharedCameraMetricsParams + setStartSubType:_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartSubTypeKey] ?: @"null"]; + [sharedCameraMetricsParams setSplits:SCDictionaryToJSONString(_cameraCreationDelaySplits)]; + + SCACameraSnapCreateDelay *creationDelay = [[SCACameraSnapCreateDelay alloc] init]; + if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] && + _cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey]) { + double startTime = [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue]; + double endTime = [_cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey] doubleValue]; + NSNumber *timeAdjustment = + _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] ?: @(0); + [creationDelay setLatencyMillis:SCTimeToMS(endTime - startTime + [timeAdjustment doubleValue])]; + } else { + [creationDelay setLatencyMillis:0]; + } + + if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey]) { + [creationDelay + setContentDurationMillis:SCTimeToMS( + [_cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey] + doubleValue])]; + } else { + [creationDelay setContentDurationMillis:0]; + } + [creationDelay setSharedCameraMetricParams:sharedCameraMetricsParams]; + [[SCLogger sharedInstance] logUserTrackedEvent:creationDelay]; +} + +@end diff --git a/Logging/SCLogger+Camera.h b/Logging/SCLogger+Camera.h new file mode 100644 index 0000000..e55c3b3 --- /dev/null +++ b/Logging/SCLogger+Camera.h @@ -0,0 +1,53 @@ +// +// SCLogger+Camera.h +// Snapchat +// +// Created by Derek Peirce on 5/8/17. +// Copyright © 2017 Snapchat, Inc. All rights reserved. +// + +#import "AVCameraViewEnums.h" + +#import +#import + +#import + +typedef NS_ENUM(NSUInteger, CameraCreationDelayLoggingStatus) { + CAMERA_CREATION_DELAY_LOGGING_START, + CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP, + CAMERA_CREATION_DELAY_LOGGING_END, +}; + +@interface SCLogger (Camera) + +@property (nonatomic, strong) NSNumber *cameraCreationDelayLoggingStatus; + +- (void)logCameraCreationStartWithMethod:(SCCameraRecordingMethod)method + lensesEnabled:(BOOL)lensesEnabled + activeLensId:(NSString *)activeLensId + captureSessionId:(NSString *)captureSessionId; +- (void)logStillImageCaptureApi:(NSString *)api; +- (void)logPreCaptureOperationRequestedAt:(CFTimeInterval)requestTime; +- (void)logPreCaptureOperationFinishedAt:(CFTimeInterval)time; +- (void)logCameraCaptureRecordingGestureFinishedAtTime:(CFTimeInterval)endRecordingTime; +- (void)logCameraCaptureFinishedWithDuration:(CFTimeInterval)duration; +- (void)logCameraCaptureContentReady; +- (void)logPreviewFinishedPreparation; +- (void)logPreviewDisplayedForImage:(BOOL)isImage; +- (void)logPreviewAnimationComplete:(BOOL)isImage; +- (void)logPreviewFirstFramePlayed:(BOOL)isImage; +- (void)cancelCameraCreationEvent; + +- (void)logRecordingMayBeTooShortWithMethod:(SCCameraRecordingMethod)method; +- (void)logRecordingWasTooShortWithFirstFrame:(CMTime)firstFrame + frontFacingCamera:(BOOL)isFrontFacing + cameraFlips:(NSInteger)cameraFlips; + +- (void)logManagedCapturerSettingFailure:(NSString *)settingTask error:(NSError *)error; +- (void)logCameraExposureAdjustmentDelayStart; +- (void)logCameraExposureAdjustmentDelayEndWithStrategy:(NSString *)strategy; +- (void)logCameraCreationDelaySubMetricsStartWithSignCode:(kSCSignPostCodeEnum)signPostCode; +- (void)logCameraCreationDelaySubMetricsEndWithSignCode:(kSCSignPostCodeEnum)signPostCod; + +@end diff --git a/Logging/SCLogger+Camera.m b/Logging/SCLogger+Camera.m new file mode 100644 index 0000000..ea2d1df --- /dev/null +++ b/Logging/SCLogger+Camera.m @@ -0,0 +1,303 @@ +// +// SCLogger+Camera.m +// Snapchat +// +// Created by Derek Peirce on 5/8/17. +// Copyright © 2017 Snapchat, Inc. All rights reserved. +// + +#import "SCLogger+Camera.h" + +#import "SCCameraTweaks.h" + +#import +#import +#import +#import +#import + +#import + +@implementation SCLogger (Camera) + +@dynamic cameraCreationDelayLoggingStatus; + +- (NSNumber *)cameraCreationDelayLoggingStatus +{ + return objc_getAssociatedObject(self, @selector(cameraCreationDelayLoggingStatus)); +} + +- (void)setCameraCreationDelayLoggingStatus:(NSNumber *)status +{ + objc_setAssociatedObject(self, @selector(cameraCreationDelayLoggingStatus), status, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)shouldLogCameraCreationDelay +{ + return [[self cameraCreationDelayLoggingStatus] intValue] != CAMERA_CREATION_DELAY_LOGGING_END; +} + +- (void)logCameraCreationDelayEnd +{ + if ([[self cameraCreationDelayLoggingStatus] intValue] == CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP) { + SCTraceSignPostEndForMetrics(kSCSignPostCameraCreationDelay, 0, 0, 0, 0); + [self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGING_END)]; + } else { + [self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP)]; + } +} + +- (void)logCameraCreationStartWithMethod:(SCCameraRecordingMethod)method + lensesEnabled:(BOOL)lensesEnabled + activeLensId:(NSString *)activeLensId + captureSessionId:(NSString *)captureSessionId +{ + NSMutableDictionary *parameters = [@{ + @"lens_ui_enabled" : @(lensesEnabled), + @"analytics_version" : kSCCameraDelayEventVersion, + @"method" : @(method), + } mutableCopy]; + if (lensesEnabled && activeLensId) { + [parameters setObject:activeLensId forKey:@"lens_id"]; + } + if (captureSessionId) { + [parameters setObject:captureSessionId forKey:@"capture_session_id"]; + } + [self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGING_START)]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCreationDelay]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraRecordingGestureFinished]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationRequested]; + [[SCLogger sharedInstance] logTimedEventStart:kSCCameraCaptureDelayEvent + uniqueId:@"" + isUniqueEvent:NO + parameters:parameters + shouldLogStartTime:YES]; +} + +- (void)logCameraExposureAdjustmentDelayStart +{ + [[SCLogger sharedInstance] logTimedEventStart:kSCCameraExposureAdjustmentDelay + uniqueId:@"" + isUniqueEvent:NO + parameters:nil + shouldLogStartTime:YES]; +} + +- (void)logCameraExposureAdjustmentDelayEndWithStrategy:(NSString *)strategy +{ + [[SCLogger sharedInstance] logTimedEventEnd:kSCCameraExposureAdjustmentDelay + uniqueId:@"" + parameters:@{ + @"strategy" : strategy + }]; +} + +- (void)logCameraCaptureRecordingGestureFinishedAtTime:(CFTimeInterval)endRecordingTime +{ + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraRecordingGestureFinished]; + [[SCLogger sharedInstance] + updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + update:^(NSMutableDictionary *startParameters) { + NSMutableDictionary *eventParameters = + startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey]; + NSNumber *recordStartTime = + (NSNumber *)eventParameters[kSCCameraSubmetricsPreCaptureOperationFinished]; + CFTimeInterval endRecordingTimeOffset = + endRecordingTime - + [startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventTimeKey] doubleValue]; + if (recordStartTime) { + CFTimeInterval timeDisplacement = + ([recordStartTime doubleValue] / 1000.0) - endRecordingTimeOffset; + [eventParameters setObject:@(timeDisplacement) + forKey:SCPerformanceMetricsKey.kSCLoggerStartEventTimeAdjustmentKey]; + } + [self addSplitPoint:kSCCameraSubmetricsRecordingGestureFinished + atTime:endRecordingTime + toEvent:startParameters]; + }]; +} + +- (void)logStillImageCaptureApi:(NSString *)api +{ + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationRequested]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationFinished]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCaptureContentReady]; + CFTimeInterval requestTime = CACurrentMediaTime(); + [self updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + update:^(NSMutableDictionary *startParameters) { + NSMutableDictionary *eventParameters = + startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey]; + [eventParameters setObject:api forKey:@"api_type"]; + [eventParameters setObject:@(1) forKey:@"camera_api_level"]; + [self addSplitPoint:@"PRE_CAPTURE_OPERATION_REQUESTED" + atTime:requestTime + toEvent:startParameters]; + }]; +} + +- (void)logPreCaptureOperationRequestedAt:(CFTimeInterval)requestTime +{ + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationRequested]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationFinished]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCaptureContentReady]; + [self updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + splitPoint:kSCCameraSubmetricsPreCaptureOperationRequested + time:requestTime]; +} + +- (void)logPreCaptureOperationFinishedAt:(CFTimeInterval)time +{ + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationFinished]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreviewPlayerReady]; + [self updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + splitPoint:kSCCameraSubmetricsPreCaptureOperationFinished + time:time]; +} + +- (void)logCameraCaptureFinishedWithDuration:(CFTimeInterval)duration +{ + [[SCLogger sharedInstance] + updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + update:^(NSMutableDictionary *startParameters) { + NSMutableDictionary *eventParameters = + startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey]; + [eventParameters setObject:@(SCTimeInMillisecond(duration)) forKey:@"content_duration"]; + }]; +} + +- (void)logCameraCaptureContentReady +{ + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraCaptureContentReady]; + [[SCLogger sharedInstance] updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + splitPoint:kSCCameraSubmetricsCameraCaptureContentReady]; +} + +- (void)logPreviewFinishedPreparation +{ + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewFinishPreparation]; + [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreviewAnimationFinish]; + [self updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + splitPoint:kSCCameraSubmetricsPreviewFinishPreparation]; +} + +- (void)logPreviewDisplayedForImage:(BOOL)isImage +{ + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewLayoutReady]; + [self updateLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@"" splitPoint:kSCCameraSubmetricsPreviewLayoutReady]; +} + +- (void)logPreviewAnimationComplete:(BOOL)isImage +{ + [self updateLogTimedEvent:kSCCameraCaptureDelayEvent + uniqueId:@"" + splitPoint:kSCCameraSubmetricsPreviewAnimationFinish]; + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewAnimationFinish]; + [self logCameraCreationDelayEnd]; + [self conditionallyLogTimedEventEnd:kSCCameraCaptureDelayEvent + uniqueId:@"" + parameters:@{ + @"type" : isImage ? @"image" : @"video", + } + shouldLog:^BOOL(NSDictionary *startParameters) { + // For video, PREVIEW_PLAYER_READY and PREVIEW_ANIMATION_FINISH can happen in either + // order. So here we check for existence of this key, and end timer if the other + // event have happened. + NSMutableDictionary *eventParameters = + startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey]; + return eventParameters[kSCCameraSubmetricsPreviewPlayerReady] != nil; + }]; +} + +- (void)logPreviewFirstFramePlayed:(BOOL)isImage +{ + [self updateLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@"" splitPoint:kSCCameraSubmetricsPreviewPlayerReady]; + [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewPlayerReady]; + [self logCameraCreationDelayEnd]; + [self conditionallyLogTimedEventEnd:kSCCameraCaptureDelayEvent + uniqueId:@"" + parameters:@{ + @"type" : isImage ? @"image" : @"video", + } + shouldLog:^BOOL(NSDictionary *startParameters) { + NSMutableDictionary *eventParameters = + startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey]; + // See the comment above for PREVIEW_PLAYER_READY and PREVIEW_ANIMATION_FINISH. + return eventParameters[kSCCameraSubmetricsPreviewAnimationFinish] != nil; + }]; +} + +- (void)cancelCameraCreationEvent +{ + [self cancelLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@""]; +} + +- (void)logRecordingMayBeTooShortWithMethod:(SCCameraRecordingMethod)method +{ + [[SCLogger sharedInstance] cancelLogTimedEvent:kSCCameraMetricsRecordingTooShort uniqueId:@""]; + [[SCLogger sharedInstance] logTimedEventStart:kSCCameraMetricsRecordingTooShort + uniqueId:@"" + isUniqueEvent:NO + parameters:@{ + @"method" : @(method), + @"analytics_version" : kSCCameraRecordingTooShortVersion, + } + shouldLogStartTime:YES]; +} + +- (void)logRecordingWasTooShortWithFirstFrame:(CMTime)firstFrame + frontFacingCamera:(BOOL)isFrontFacing + cameraFlips:(NSInteger)cameraFlips +{ + [self logTimedEventEnd:kSCCameraMetricsRecordingTooShort + uniqueId:@"" + update:^(NSDictionary *startParameters, CFTimeInterval eventEndTime, CFTimeInterval adjustedTime) { + NSMutableDictionary *eventParameters = + startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey]; + if (CMTIME_IS_VALID(firstFrame)) { + CFTimeInterval startTime = + [startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventTimeKey] doubleValue]; + CFTimeInterval firstFrameRelative = CMTimeGetSeconds(firstFrame) - startTime; + [eventParameters setObject:@(firstFrameRelative) forKey:@"first_frame_s"]; + } + [eventParameters setObject:@(isFrontFacing) forKey:@"is_front_facing"]; + if (cameraFlips) { + [eventParameters setObject:@(cameraFlips > 0) forKey:@"has_camera_been_flipped"]; + } + }]; +} + +- (void)logManagedCapturerSettingFailure:(NSString *)settingTask error:(NSError *)error +{ + NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; + parameters[@"setting_task"] = settingTask; + if (error) { + parameters[@"setting error"] = error; + } + [[SCLogger sharedInstance] logTimedEventEnd:kSCCameraManagedCaptureSettingFailure + uniqueId:@"" + parameters:parameters]; +} + +- (void)logCameraCreationDelaySubMetricsStartWithSignCode:(kSCSignPostCodeEnum)signPostCode +{ + if ([self shouldLogCameraCreationDelay]) { + SCTraceSignPostStartForMetrics(signPostCode, 0, 0, 0, 0); + } +} + +- (void)logCameraCreationDelaySubMetricsEndWithSignCode:(kSCSignPostCodeEnum)signPostCode +{ + if ([self shouldLogCameraCreationDelay]) { + SCTraceSignPostEndForMetrics(signPostCode, 0, 0, 0, 0); + } +} + +@end diff --git a/Logging/SCManiphestTicketCreator.h b/Logging/SCManiphestTicketCreator.h new file mode 100644 index 0000000..717ff28 --- /dev/null +++ b/Logging/SCManiphestTicketCreator.h @@ -0,0 +1,24 @@ +// +// SCManiphestTicketCreator.h +// SCCamera +// +// Created by Michel Loenngren on 4/16/18. +// + +#import + +/** + Protocol for filing jira tickets and beta s2r. + */ +@protocol SCManiphestTicketCreator + +- (void)createAndFile:(NSData *)image + creationTime:(long)reportCreationTime + description:(NSString *)bugDescription + email:(NSString *)otherEmail + project:(NSString *)projectName + subproject:(NSString *)subprojectName; + +- (void)createAndFileBetaReport:(NSString *)msg; + +@end