diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index e68b38cabe..16b967d677 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -335,6 +335,15 @@ private final class ConferenceCallE2EContextStateImpl: ConferenceCallE2EContextS func getParticipants() -> [ConferenceCallE2EContext.BlockchainParticipant] { return self.call.participants().map { ConferenceCallE2EContext.BlockchainParticipant(userId: $0.userId, internalId: $0.internalId) } } + + func getParticipantLatencies() -> [Int64: Double] { + let dict = self.call.participantLatencies() + var result: [Int64: Double] = [:] + for (k, v) in dict { + result[k.int64Value] = v.doubleValue + } + return result + } func getParticipantIds() -> [Int64] { return self.call.participants().map { $0.userId } diff --git a/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift b/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift index 2015fd0755..ad71c3ea4f 100644 --- a/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift +++ b/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift @@ -5,6 +5,7 @@ public protocol ConferenceCallE2EContextState: AnyObject { func getEmojiState() -> Data? func getParticipantIds() -> [Int64] func getParticipants() -> [ConferenceCallE2EContext.BlockchainParticipant] + func getParticipantLatencies() -> [Int64: Double] func applyBlock(block: Data) func applyBroadcastBlock(block: Data) @@ -166,7 +167,7 @@ public final class ConferenceCallE2EContext { let keyPair = self.keyPair let userId = self.userId let initializeState = self.initializeState - let (outBlocks, outEmoji, outBlockchainParticipants) = self.state.with({ callState -> ([Data], Data, [BlockchainParticipant]) in + let (outBlocks, outEmoji, outBlockchainParticipants, participantLatencies) = self.state.with({ callState -> ([Data], Data, [BlockchainParticipant], [Int64: Double]) in if let state = callState.state { for block in blocks { if subChainId == 0 { @@ -175,26 +176,26 @@ public final class ConferenceCallE2EContext { state.applyBroadcastBlock(block: block) } } - return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants()) + return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants(), state.getParticipantLatencies()) } else { if subChainId == 0 { guard let block = blocks.last else { - return ([], Data(), []) + return ([], Data(), [], [:]) } guard let state = initializeState(keyPair, userId, block) else { - return ([], Data(), []) + return ([], Data(), [], [:]) } callState.state = state for block in callState.pendingIncomingBroadcastBlocks { state.applyBroadcastBlock(block: block) } callState.pendingIncomingBroadcastBlocks.removeAll() - return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants()) + return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants(), state.getParticipantLatencies()) } else if subChainId == 1 { callState.pendingIncomingBroadcastBlocks.append(contentsOf: blocks) - return ([], Data(), []) + return ([], Data(), [], [:]) } else { - return ([], Data(), []) + return ([], Data(), [], [:]) } } }) @@ -204,6 +205,10 @@ public final class ConferenceCallE2EContext { for outBlock in outBlocks { let _ = self.engine.calls.sendConferenceCallBroadcast(callId: self.callId, accessHash: self.accessHash, block: outBlock).startStandalone() } + + #if DEBUG + print("Latencies: \(participantLatencies)") + #endif } private func e2ePoll(subChainId: Int) { diff --git a/third-party/td/TdBinding/Public/TdBinding/TdBinding.h b/third-party/td/TdBinding/Public/TdBinding/TdBinding.h index 79df862c39..3ec2d6cf0f 100644 --- a/third-party/td/TdBinding/Public/TdBinding/TdBinding.h +++ b/third-party/td/TdBinding/Public/TdBinding/TdBinding.h @@ -37,6 +37,8 @@ NS_ASSUME_NONNULL_BEGIN - (NSData *)emojiState; - (NSArray *)participants; +- (NSDictionary *)participantLatencies; + - (void)applyBlock:(NSData *)block; - (void)applyBroadcastBlock:(NSData *)block; diff --git a/third-party/td/TdBinding/Sources/TdBinding.mm b/third-party/td/TdBinding/Sources/TdBinding.mm index cd60c2e2b5..d7f5f1f369 100644 --- a/third-party/td/TdBinding/Sources/TdBinding.mm +++ b/third-party/td/TdBinding/Sources/TdBinding.mm @@ -190,6 +190,52 @@ static NSString *hexStringFromData(NSData *data) { return participants; } +- (NSDictionary *)participantLatencies { + auto describeResult = tde2e_api::call_describe(_callId); + if (describeResult.is_ok()) { + NSString *string = [[NSString alloc] initWithData:[NSData dataWithBytes:describeResult.value().data() length:describeResult.value().size()] encoding:NSASCIIStringEncoding]; + + NSRegularExpression *pairRe = [NSRegularExpression regularExpressionWithPattern:@"(\\d+):(\\d+\\.\\d+)s" options:0 error:NULL]; + + NSMutableDictionary *commitTimes = [NSMutableDictionary dictionary]; + NSMutableDictionary *revealTimes = [NSMutableDictionary dictionary]; + + // split into lines and look for the two lines + [string enumerateLinesUsingBlock:^(NSString * _Nonnull line, BOOL * _Nonnull stop) { + if ([line containsString:@"commit ="]) { + [pairRe enumerateMatchesInString:line options:0 range:NSMakeRange(0, line.length) usingBlock:^(NSTextCheckingResult * _Nullable match, NSMatchingFlags flags, BOOL * _Nonnull stop) { + NSString *userIdStr = [line substringWithRange:[match rangeAtIndex:1]]; + NSString *durStr = [line substringWithRange:[match rangeAtIndex:2]]; + NSNumber *uid = @([userIdStr longLongValue]); + NSNumber *dur = @([durStr doubleValue]); + commitTimes[uid] = dur; + }]; + } + else if ([line containsString:@"reveal ="]) { + [pairRe enumerateMatchesInString:line options:0 range:NSMakeRange(0, line.length) usingBlock:^(NSTextCheckingResult * _Nullable match, NSMatchingFlags flags, BOOL * _Nonnull stop) { + NSString *userIdStr = [line substringWithRange:[match rangeAtIndex:1]]; + NSString *durStr = [line substringWithRange:[match rangeAtIndex:2]]; + NSNumber *uid = @([userIdStr longLongValue]); + NSNumber *dur = @([durStr doubleValue]); + revealTimes[uid] = dur; + }]; + } + }]; + + // build final result = commit+reveal + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + for (NSNumber *uid in commitTimes) { + double commit = commitTimes[uid].doubleValue; + double reveal = revealTimes[uid].doubleValue; // will be 0 if missing + result[uid] = @(commit + reveal); + } + + return result; + } + + return @{}; +} + - (void)applyBlock:(NSData *)block { std::string mappedBlock((uint8_t *)block.bytes, ((uint8_t *)block.bytes) + block.length);