You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
14 KiB
304 lines
14 KiB
//
|
|
// SCCoreCameraLogger.m
|
|
// Snapchat
|
|
//
|
|
// Created by Chao Pang on 3/6/18.
|
|
//
|
|
|
|
#import "SCCoreCameraLogger.h"
|
|
|
|
#import <BlizzardSchema/SCAEvents.h>
|
|
#import <SCFoundation/SCQueuePerformer.h>
|
|
#import <SCGhostToSnappable/SCGhostToSnappableSignal.h>
|
|
#import <SCLogger/SCCameraMetrics+CameraCreationDelay.h>
|
|
|
|
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
|