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