diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index 23ca353f58..fe8b970b53 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -172,6 +172,7 @@ public struct PresentationGroupCallState: Equatable { case muted } + public var myPeerId: PeerId public var networkState: NetworkState public var canManageCall: Bool public var adminIds: Set @@ -180,6 +181,7 @@ public struct PresentationGroupCallState: Equatable { public var recordingStartTimestamp: Int32? public init( + myPeerId: PeerId, networkState: NetworkState, canManageCall: Bool, adminIds: Set, @@ -187,6 +189,7 @@ public struct PresentationGroupCallState: Equatable { defaultParticipantMuteState: DefaultParticipantMuteState?, recordingStartTimestamp: Int32? ) { + self.myPeerId = myPeerId self.networkState = networkState self.canManageCall = canManageCall self.adminIds = adminIds @@ -282,7 +285,6 @@ public protocol PresentationGroupCall: class { var accountContext: AccountContext { get } var internalId: CallSessionInternalId { get } var peerId: PeerId { get } - var myPeerId: PeerId { get } var isVideo: Bool { get } @@ -298,10 +300,12 @@ public protocol PresentationGroupCall: class { var memberEvents: Signal { get } + func reconnect(as peerId: PeerId) func leave(terminateIfPossible: Bool) -> Signal func toggleIsMuted() func setIsMuted(action: PresentationGroupCallMuteAction) + func raiseHand() func requestVideo() func disableVideo() func updateDefaultParticipantsAreMuted(isMuted: Bool) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 5727467be5..5746e7cb7f 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -142,7 +142,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) } dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) } dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) } - dict[-763544865] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) } + dict[430815881] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) } dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) } dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) } dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) } @@ -193,7 +193,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1318109142] = { return Api.Update.parse_updateMessageID($0) } dict[-1576161051] = { return Api.Update.parse_updateDeleteMessages($0) } dict[1548249383] = { return Api.Update.parse_updateUserTyping($0) } - dict[-1704596961] = { return Api.Update.parse_updateChatUserTyping($0) } + dict[-2033525908] = { return Api.Update.parse_updateChatUserTyping($0) } dict[125178264] = { return Api.Update.parse_updateChatParticipants($0) } dict[469489699] = { return Api.Update.parse_updateUserStatus($0) } dict[-1489818765] = { return Api.Update.parse_updateUserName($0) } @@ -270,7 +270,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[482860628] = { return Api.Update.parse_updateReadChannelDiscussionInbox($0) } dict[1178116716] = { return Api.Update.parse_updateReadChannelDiscussionOutbox($0) } dict[610945826] = { return Api.Update.parse_updatePeerBlocked($0) } - dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($0) } + dict[1796675352] = { return Api.Update.parse_updateChannelUserTyping($0) } dict[-309990731] = { return Api.Update.parse_updatePinnedMessages($0) } dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) } dict[321954198] = { return Api.Update.parse_updateChat($0) } diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 33e657b206..8ec11d5de7 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -3604,13 +3604,13 @@ public extension Api { } public enum GroupCallParticipant: TypeConstructorDescription { - case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?) + case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?, raiseHandRating: Int64?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about): + case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating): if boxed { - buffer.appendInt32(-763544865) + buffer.appendInt32(430815881) } serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) @@ -3619,14 +3619,15 @@ public extension Api { serializeInt32(source, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 7) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 11) != 0 {serializeString(about!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 13) != 0 {serializeInt64(raiseHandRating!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about): - return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about)]) + case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating): + return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about), ("raiseHandRating", raiseHandRating)]) } } @@ -3647,6 +3648,8 @@ public extension Api { if Int(_1!) & Int(1 << 7) != 0 {_6 = reader.readInt32() } var _7: String? if Int(_1!) & Int(1 << 11) != 0 {_7 = parseString(reader) } + var _8: Int64? + if Int(_1!) & Int(1 << 13) != 0 {_8 = reader.readInt64() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -3654,8 +3657,9 @@ public extension Api { let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7) + let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8) } else { return nil @@ -4586,7 +4590,7 @@ public extension Api { case updateMessageID(id: Int32, randomId: Int64) case updateDeleteMessages(messages: [Int32], pts: Int32, ptsCount: Int32) case updateUserTyping(userId: Int32, action: Api.SendMessageAction) - case updateChatUserTyping(chatId: Int32, userId: Int32, action: Api.SendMessageAction) + case updateChatUserTyping(chatId: Int32, fromId: Api.Peer, action: Api.SendMessageAction) case updateChatParticipants(participants: Api.ChatParticipants) case updateUserStatus(userId: Int32, status: Api.UserStatus) case updateUserName(userId: Int32, firstName: String, lastName: String, username: String) @@ -4663,7 +4667,7 @@ public extension Api { case updateReadChannelDiscussionInbox(flags: Int32, channelId: Int32, topMsgId: Int32, readMaxId: Int32, broadcastId: Int32?, broadcastPost: Int32?) case updateReadChannelDiscussionOutbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32) case updatePeerBlocked(peerId: Api.Peer, blocked: Api.Bool) - case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction) + case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, fromId: Api.Peer, action: Api.SendMessageAction) case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32) case updatePinnedChannelMessages(flags: Int32, channelId: Int32, messages: [Int32], pts: Int32, ptsCount: Int32) case updateChat(chatId: Int32) @@ -4710,12 +4714,12 @@ public extension Api { serializeInt32(userId, buffer: buffer, boxed: false) action.serialize(buffer, true) break - case .updateChatUserTyping(let chatId, let userId, let action): + case .updateChatUserTyping(let chatId, let fromId, let action): if boxed { - buffer.appendInt32(-1704596961) + buffer.appendInt32(-2033525908) } serializeInt32(chatId, buffer: buffer, boxed: false) - serializeInt32(userId, buffer: buffer, boxed: false) + fromId.serialize(buffer, true) action.serialize(buffer, true) break case .updateChatParticipants(let participants): @@ -5359,14 +5363,14 @@ public extension Api { peerId.serialize(buffer, true) blocked.serialize(buffer, true) break - case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action): + case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action): if boxed { - buffer.appendInt32(-13975905) + buffer.appendInt32(1796675352) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(channelId, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} - serializeInt32(userId, buffer: buffer, boxed: false) + fromId.serialize(buffer, true) action.serialize(buffer, true) break case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): @@ -5480,8 +5484,8 @@ public extension Api { return ("updateDeleteMessages", [("messages", messages), ("pts", pts), ("ptsCount", ptsCount)]) case .updateUserTyping(let userId, let action): return ("updateUserTyping", [("userId", userId), ("action", action)]) - case .updateChatUserTyping(let chatId, let userId, let action): - return ("updateChatUserTyping", [("chatId", chatId), ("userId", userId), ("action", action)]) + case .updateChatUserTyping(let chatId, let fromId, let action): + return ("updateChatUserTyping", [("chatId", chatId), ("fromId", fromId), ("action", action)]) case .updateChatParticipants(let participants): return ("updateChatParticipants", [("participants", participants)]) case .updateUserStatus(let userId, let status): @@ -5634,8 +5638,8 @@ public extension Api { return ("updateReadChannelDiscussionOutbox", [("channelId", channelId), ("topMsgId", topMsgId), ("readMaxId", readMaxId)]) case .updatePeerBlocked(let peerId, let blocked): return ("updatePeerBlocked", [("peerId", peerId), ("blocked", blocked)]) - case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action): - return ("updateChannelUserTyping", [("flags", flags), ("channelId", channelId), ("topMsgId", topMsgId), ("userId", userId), ("action", action)]) + case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action): + return ("updateChannelUserTyping", [("flags", flags), ("channelId", channelId), ("topMsgId", topMsgId), ("fromId", fromId), ("action", action)]) case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): return ("updatePinnedMessages", [("flags", flags), ("peer", peer), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)]) case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): @@ -5728,8 +5732,10 @@ public extension Api { public static func parse_updateChatUserTyping(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } var _3: Api.SendMessageAction? if let signature = reader.readInt32() { _3 = Api.parse(reader, signature: signature) as? Api.SendMessageAction @@ -5738,7 +5744,7 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil if _c1 && _c2 && _c3 { - return Api.Update.updateChatUserTyping(chatId: _1!, userId: _2!, action: _3!) + return Api.Update.updateChatUserTyping(chatId: _1!, fromId: _2!, action: _3!) } else { return nil @@ -7028,8 +7034,10 @@ public extension Api { _2 = reader.readInt32() var _3: Int32? if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } - var _4: Int32? - _4 = reader.readInt32() + var _4: Api.Peer? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Peer + } var _5: Api.SendMessageAction? if let signature = reader.readInt32() { _5 = Api.parse(reader, signature: signature) as? Api.SendMessageAction @@ -7040,7 +7048,7 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateChannelUserTyping(flags: _1!, channelId: _2!, topMsgId: _3, userId: _4!, action: _5!) + return Api.Update.updateChannelUserTyping(flags: _1!, channelId: _2!, topMsgId: _3, fromId: _4!, action: _5!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 7ebbbe2ffa..48bd6335f2 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -7595,14 +7595,12 @@ public extension Api { }) } - public static func createGroupCall(flags: Int32, peer: Api.InputPeer, joinAs: Api.InputPeer?, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func createGroupCall(peer: Api.InputPeer, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(534090322) - serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(-1120031776) peer.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {joinAs!.serialize(buffer, true)} serializeInt32(randomId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "phone.createGroupCall", parameters: [("flags", flags), ("peer", peer), ("joinAs", joinAs), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + return (FunctionDescription(name: "phone.createGroupCall", parameters: [("peer", peer), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { @@ -7612,12 +7610,12 @@ public extension Api { }) } - public static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(780232376) + buffer.appendInt32(909354416) serializeInt32(flags, buffer: buffer, boxed: false) call.serialize(buffer, true) - if Int(flags) & Int(1 << 1) != 0 {joinAs!.serialize(buffer, true)} + joinAs.serialize(buffer, true) params.serialize(buffer, true) return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", flags), ("call", call), ("joinAs", joinAs), ("params", params)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) @@ -7707,14 +7705,14 @@ public extension Api { }) } - public static func getGroupParticipants(call: Api.InputGroupCall, ids: [Int32], sources: [Int32], offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func getGroupParticipants(call: Api.InputGroupCall, ids: [Api.InputPeer], sources: [Int32], offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-906898811) + buffer.appendInt32(-984033109) call.serialize(buffer, true) buffer.appendInt32(481674261) buffer.appendInt32(Int32(ids.count)) for item in ids { - serializeInt32(item, buffer: buffer, boxed: false) + item.serialize(buffer, true) } buffer.appendInt32(481674261) buffer.appendInt32(Int32(sources.count)) @@ -7764,14 +7762,15 @@ public extension Api { }) } - public static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, volume: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, volume: Int32?, raiseHand: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1192486819) + buffer.appendInt32(-646583424) serializeInt32(flags, buffer: buffer, boxed: false) call.serialize(buffer, true) participant.serialize(buffer, true) if Int(flags) & Int(1 << 1) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "phone.editGroupCallParticipant", parameters: [("flags", flags), ("call", call), ("participant", participant), ("volume", volume)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + if Int(flags) & Int(1 << 2) != 0 {raiseHand!.serialize(buffer, true)} + return (FunctionDescription(name: "phone.editGroupCallParticipant", parameters: [("flags", flags), ("call", call), ("participant", participant), ("volume", volume), ("raiseHand", raiseHand)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 7c41b574eb..f96ad7a1cf 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -19,7 +19,9 @@ import DeviceProximity private extension GroupCallParticipantsContext.Participant { var allSsrcs: Set { var participantSsrcs = Set() - participantSsrcs.insert(self.ssrc) + if let ssrc = self.ssrc { + participantSsrcs.insert(ssrc) + } if let jsonParams = self.jsonParams, let jsonData = jsonParams.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] { if let groups = json["ssrc-groups"] as? [Any] { for group in groups { @@ -93,6 +95,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { let context = GroupCallParticipantsContext( account: account, peerId: peerId, + myPeerId: account.peerId, id: call.id, accessHash: call.accessHash, state: state @@ -191,8 +194,9 @@ public final class AccountGroupCallContextCacheImpl: AccountGroupCallContextCach } private extension PresentationGroupCallState { - static var initialValue: PresentationGroupCallState { + static func initialValue(myPeerId: PeerId) -> PresentationGroupCallState { return PresentationGroupCallState( + myPeerId: myPeerId, networkState: .connecting, canManageCall: false, adminIds: Set(), @@ -207,7 +211,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private enum InternalState { case requesting case active(GroupCallInfo) - case estabilished(info: GroupCallInfo, clientParams: String, localSsrc: UInt32, initialState: GroupCallParticipantsContext.State) + case established(info: GroupCallInfo, connectionMode: JoinGroupCallResult.ConnectionMode, clientParams: String, localSsrc: UInt32, initialState: GroupCallParticipantsContext.State) var callInfo: GroupCallInfo? { switch self { @@ -215,7 +219,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { return nil case let .active(info): return info - case let .estabilished(info, _, _, _): + case let .established(info, _, _, _, _): return info } } @@ -337,8 +341,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private var initialCall: CachedChannelData.ActiveCall? public let internalId: CallSessionInternalId public let peerId: PeerId - private let joinAsPeerId: PeerId - public let myPeerId: PeerId + private var joinAsPeerId: PeerId public let peer: Peer? public private(set) var isVideo: Bool @@ -424,14 +427,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private let wasRemoved = Promise(false) - private var stateValue = PresentationGroupCallState.initialValue { + private var stateValue: PresentationGroupCallState { didSet { if self.stateValue != oldValue { self.statePromise.set(self.stateValue) } } } - private let statePromise = ValuePromise(PresentationGroupCallState.initialValue) + private let statePromise: ValuePromise public var state: Signal { return self.statePromise.get() } @@ -522,7 +525,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.peerId = peerId self.peer = peer self.joinAsPeerId = joinAsPeerId ?? accountContext.account.peerId - self.myPeerId = joinAsPeerId ?? accountContext.account.peerId + + self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId) + self.statePromise = ValuePromise(self.stateValue) self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) @@ -630,7 +635,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { guard let strongSelf = self else { return } - if case let .estabilished(callInfo, _, _, _) = strongSelf.internalState { + if case let .established(callInfo, _, _, _, _) = strongSelf.internalState { var addedParticipants: [(UInt32, String?)] = [] var removedSsrc: [UInt32] = [] for (callId, update) in updates { @@ -639,21 +644,38 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { case let .state(update): for participantUpdate in update.participantUpdates { if case .left = participantUpdate.participationStatusChange { - removedSsrc.append(participantUpdate.ssrc) + if let ssrc = participantUpdate.ssrc { + removedSsrc.append(ssrc) + } if participantUpdate.peerId == strongSelf.joinAsPeerId { - if case let .estabilished(_, _, ssrc, _) = strongSelf.internalState, ssrc == participantUpdate.ssrc { + if case let .established(_, _, _, ssrc, _) = strongSelf.internalState, ssrc == participantUpdate.ssrc { strongSelf._canBeRemoved.set(.single(true)) } } } else if participantUpdate.peerId == strongSelf.joinAsPeerId { - if case let .estabilished(_, _, ssrc, _) = strongSelf.internalState, ssrc != participantUpdate.ssrc { - strongSelf._canBeRemoved.set(.single(true)) + if case let .established(_, connectionMode, _, ssrc, _) = strongSelf.internalState { + if ssrc != participantUpdate.ssrc { + strongSelf._canBeRemoved.set(.single(true)) + } else if case .broadcast = connectionMode { + let canUnmute: Bool + if let muteState = participantUpdate.muteState { + canUnmute = muteState.canUnmute + } else { + canUnmute = true + } + + if canUnmute { + strongSelf.requestCall() + } + } } } else if case .joined = participantUpdate.participationStatusChange { - addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams)) - } else if strongSelf.ssrcMapping[participantUpdate.ssrc] == nil { - addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams)) + if let ssrc = participantUpdate.ssrc { + addedParticipants.append((ssrc, participantUpdate.jsonParams)) + } + } else if let ssrc = participantUpdate.ssrc, strongSelf.ssrcMapping[ssrc] == nil { + addedParticipants.append((ssrc, participantUpdate.jsonParams)) } } case let .call(isTerminated, _, _, _): @@ -723,9 +745,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if let myPeer = myPeer { participants.append(GroupCallParticipantsContext.Participant( peer: myPeer, - ssrc: 0, + ssrc: nil, jsonParams: nil, joinTimestamp: strongSelf.temporaryJoinTimestamp, + raiseHandRating: nil, activityTimestamp: nil, activityRank: nil, muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), @@ -848,29 +871,43 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { break default: if case let .active(callInfo) = internalState { - let callContext = OngoingGroupCallContext(video: self.videoCapturer, participantDescriptionsRequired: { [weak self] ssrcs in - Queue.mainQueue().async { + let callContext: OngoingGroupCallContext + if let current = self.callContext { + callContext = current + } else { + callContext = OngoingGroupCallContext(video: self.videoCapturer, participantDescriptionsRequired: { [weak self] ssrcs in + Queue.mainQueue().async { + guard let strongSelf = self else { + return + } + strongSelf.maybeRequestParticipants(ssrcs: ssrcs) + } + }, audioStreamData: OngoingGroupCallContext.AudioStreamData(account: self.accountContext.account, callId: callInfo.id, accessHash: callInfo.accessHash, datacenterId: callInfo.streamDcId.flatMap(Int.init)), rejoinNeeded: { [weak self] in + Queue.mainQueue().async { + guard let strongSelf = self else { + return + } + if case .established = strongSelf.internalState { + //strongSelf.requestCall() + } + } + }) + self.incomingVideoSourcePromise.set(callContext.videoSources + |> deliverOnMainQueue + |> map { [weak self] sources -> [PeerId: UInt32] in guard let strongSelf = self else { - return + return [:] } - strongSelf.maybeRequestParticipants(ssrcs: ssrcs) - } - }, audioStreamData: OngoingGroupCallContext.AudioStreamData(account: self.accountContext.account, callId: callInfo.id, accessHash: callInfo.accessHash, datacenterId: callInfo.streamDcId.flatMap(Int.init))) - self.incomingVideoSourcePromise.set(callContext.videoSources - |> deliverOnMainQueue - |> map { [weak self] sources -> [PeerId: UInt32] in - guard let strongSelf = self else { - return [:] - } - var result: [PeerId: UInt32] = [:] - for source in sources { - if let peerId = strongSelf.ssrcMapping[source] { - result[peerId] = source + var result: [PeerId: UInt32] = [:] + for source in sources { + if let peerId = strongSelf.ssrcMapping[source] { + result[peerId] = source + } } - } - return result - }) - self.callContext = callContext + return result + }) + self.callContext = callContext + } self.joinDisposable.set((callContext.joinPayload |> distinctUntilChanged(isEqual: { lhs, rhs in if lhs.0 != rhs.0 { @@ -900,14 +937,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } if let clientParams = joinCallResult.callInfo.clientParams { strongSelf.ssrcMapping.removeAll() - var addedParticipants: [(UInt32, String?)] = [] + let addedParticipants: [(UInt32, String?)] = [] for participant in joinCallResult.state.participants { - strongSelf.ssrcMapping[participant.ssrc] = participant.peer.id - //addedParticipants.append((participant.ssrc, participant.jsonParams)) + if let ssrc = participant.ssrc { + strongSelf.ssrcMapping[ssrc] = participant.peer.id + //addedParticipants.append((participant.ssrc, participant.jsonParams)) + } } - strongSelf.callContext?.setJoinResponse(payload: clientParams, participants: addedParticipants) - strongSelf.updateSessionState(internalState: .estabilished(info: joinCallResult.callInfo, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: strongSelf.audioSessionControl) + switch joinCallResult.connectionMode { + case .rtc: + strongSelf.callContext?.setConnectionMode(.rtc) + strongSelf.callContext?.setJoinResponse(payload: clientParams, participants: addedParticipants) + case .broadcast: + strongSelf.callContext?.setConnectionMode(.broadcast) + } + + strongSelf.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: strongSelf.audioSessionControl) } }, error: { error in guard let strongSelf = self else { @@ -1028,10 +1074,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } switch previousInternalState { - case .estabilished: + case .established: break default: - if case let .estabilished(callInfo, clientParams, _, initialState) = internalState { + if case let .established(callInfo, _, _, _, initialState) = internalState { self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo))) self.stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId) @@ -1072,17 +1118,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { let participantsContext = GroupCallParticipantsContext( account: self.accountContext.account, peerId: self.peerId, + myPeerId: self.joinAsPeerId, id: callInfo.id, accessHash: callInfo.accessHash, state: initialState ) self.participantsContext = participantsContext + let myPeerId = self.joinAsPeerId + let myPeer = self.accountContext.account.postbox.transaction { transaction -> Peer? in + return transaction.getPeer(myPeerId) + } self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(), participantsContext.state, participantsContext.activeSpeakers, self.speakingParticipantsContext.get(), - adminIds - ).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds in + adminIds, + myPeer + ).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeer in guard let strongSelf = self else { return } @@ -1120,14 +1172,35 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { var updatedInvitedPeers = strongSelf.invitedPeersValue var didUpdateInvitedPeers = false - for participant in state.participants { + var participants = state.participants + if !participants.contains(where: { $0.peer.id == myPeerId }) { + if let myPeer = myPeer { + participants.append(GroupCallParticipantsContext.Participant( + peer: myPeer, + ssrc: nil, + jsonParams: nil, + joinTimestamp: strongSelf.temporaryJoinTimestamp, + raiseHandRating: nil, + activityTimestamp: nil, + activityRank: nil, + muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + volume: nil, + about: nil + )) + participants.sort() + } + } + + for participant in participants { members.participants.append(participant) if topParticipants.count < 3 { topParticipants.append(participant) } - strongSelf.ssrcMapping[participant.ssrc] = participant.peer.id + if let ssrc = participant.ssrc { + strongSelf.ssrcMapping[ssrc] = participant.peer.id + } if participant.peer.id == strongSelf.joinAsPeerId { if let muteState = participant.muteState { @@ -1152,10 +1225,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.callContext?.setIsMuted(true) } } else { - if let volume = participant.volume { - strongSelf.callContext?.setVolume(ssrc: participant.ssrc, volume: Double(volume) / 10000.0) - } else if participant.muteState?.mutedByYou == true { - strongSelf.callContext?.setVolume(ssrc: participant.ssrc, volume: 0.0) + if let ssrc = participant.ssrc { + if let volume = participant.volume { + strongSelf.callContext?.setVolume(ssrc: ssrc, volume: Double(volume) / 10000.0) + } else if participant.muteState?.mutedByYou == true { + strongSelf.callContext?.setVolume(ssrc: ssrc, volume: 0.0) + } } } @@ -1230,7 +1305,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { missingSsrcs.subtract(participantSsrcs) self.processedMissingSsrcs.formUnion(participantSsrcs) - addedParticipants.append((participant.ssrc, participant.jsonParams)) + if let ssrc = participant.ssrc { + addedParticipants.append((ssrc, participant.jsonParams)) + } } } } @@ -1252,7 +1329,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if self.missingSsrcs.isEmpty { return } - if case let .estabilished(callInfo, _, _, _) = self.internalState { + if case let .established(callInfo, _, _, _, _) = self.internalState { self.isRequestingMissingSsrcs = true let requestedSsrcs = self.missingSsrcs @@ -1267,7 +1344,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { var addedParticipants: [(UInt32, String?)] = [] for participant in state.participants { - addedParticipants.append((participant.ssrc, participant.jsonParams)) + if let ssrc = participant.ssrc { + addedParticipants.append((ssrc, participant.jsonParams)) + } } if !addedParticipants.isEmpty { @@ -1283,7 +1362,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if self.checkCallDisposable != nil { return } - if case let .estabilished(callInfo, _, ssrc, _) = self.internalState { + if case let .established(callInfo, connectionMode, _, ssrc, _) = self.internalState, case .rtc = connectionMode { let checkSignal = checkGroupCall(account: self.account, callId: callInfo.id, accessHash: callInfo.accessHash, ssrc: Int32(bitPattern: ssrc)) self.checkCallDisposable = (( @@ -1346,8 +1425,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } + public func reconnect(as peerId: PeerId) { + if peerId == self.joinAsPeerId { + return + } + self.joinAsPeerId = peerId + self.stateValue.myPeerId = peerId + self.requestCall() + } + public func leave(terminateIfPossible: Bool) -> Signal { - if case let .estabilished(callInfo, _, localSsrc, _) = self.internalState { + if case let .established(callInfo, _, _, localSsrc, _) = self.internalState { if terminateIfPossible { self.leaveDisposable.set((stopGroupCall(account: self.account, peerId: self.peerId, callId: callInfo.id, accessHash: callInfo.accessHash) |> deliverOnMainQueue).start(completed: { [weak self] in @@ -1415,6 +1503,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } + public func raiseHand() { + guard let membersValue = self.membersValue else { + return + } + for participant in membersValue.participants { + if participant.peer.id == self.joinAsPeerId { + if participant.raiseHandRating != nil { + return + } + + break + } + } + + self.participantsContext?.raiseHand() + } + public func requestVideo() { if self.videoCapturer == nil { let videoCapturer = OngoingCallVideoCapturer() @@ -1576,8 +1681,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } private func requestCall() { - self.callContext?.stop() - self.callContext = nil + self.callContext?.setConnectionMode(.none) self.missingSsrcsDisposable.set(nil) self.missingSsrcs.removeAll() @@ -1614,7 +1718,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } + self.networkStateDisposable.set(nil) self.joinDisposable.set(nil) + + self.checkCallDisposable?.dispose() + self.checkCallDisposable = nil + + self.stateValue.networkState = .connecting + self.requestDisposable.set((currentOrRequestedCall |> deliverOnMainQueue).start(next: { [weak self] value in guard let strongSelf = self else { @@ -1632,7 +1743,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } public func invitePeer(_ peerId: PeerId) -> Bool { - guard case let .estabilished(callInfo, _, _, _) = self.internalState, !self.invitedPeersValue.contains(peerId) else { + guard case let .established(callInfo, _, _, _, _) = self.internalState, !self.invitedPeersValue.contains(peerId) else { return false } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index aa4f8d8279..5b113520bc 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -370,8 +370,8 @@ public final class VoiceChatController: ViewController { } var peer: Peer - var myPeerId: PeerId - var ssrc: UInt32 + var myPeerId: PeerId? + var ssrc: UInt32? var presence: TelegramUserPresence? var activityTimestamp: Int32 var state: State @@ -379,6 +379,7 @@ public final class VoiceChatController: ViewController { var revealed: Bool? var canManageCall: Bool var volume: Int32? + var raiseHandStatus: Bool var stableId: PeerId { return self.peer.id @@ -415,6 +416,9 @@ public final class VoiceChatController: ViewController { if lhs.volume != rhs.volume { return false } + if lhs.raiseHandStatus != rhs.raiseHandStatus { + return false + } return true } @@ -508,7 +512,7 @@ public final class VoiceChatController: ViewController { case let .peer(peerEntry): let peer = peerEntry.peer - let text: VoiceChatParticipantItem.ParticipantText + var text: VoiceChatParticipantItem.ParticipantText let icon: VoiceChatParticipantItem.Icon switch peerEntry.state { case .listening: @@ -542,10 +546,18 @@ public final class VoiceChatController: ViewController { icon = .invite(true) } + if peerEntry.raiseHandStatus, case let .text(value, type) = text { + text = .text("[hand] \(value)", type) + } + let revealOptions: [VoiceChatParticipantItem.RevealOption] = [] return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: peerEntry.presence, text: text, icon: icon, enabled: true, selectable: peer.id != peerEntry.myPeerId || peerEntry.canManageCall, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: { - return interaction.getPeerVideo(peerEntry.ssrc) + if let ssrc = peerEntry.ssrc { + return interaction.getPeerVideo(ssrc) + } else { + return nil + } }, revealOptions: revealOptions, revealed: peerEntry.revealed, setPeerIdWithRevealedOptions: { peerId, fromPeerId in interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId) }, action: { node in @@ -771,15 +783,16 @@ public final class VoiceChatController: ViewController { switch entry { case let .peer(peer): if peer.peer.id == peerId { - let source = peer.ssrc - if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source { - strongSelf.currentDominantSpeakerWithVideo = (peerId, source) - strongSelf.call.setFullSizeVideo(peerId: peerId) - strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source)) - } else { - strongSelf.currentDominantSpeakerWithVideo = nil - strongSelf.call.setFullSizeVideo(peerId: nil) - strongSelf.mainVideoContainer?.updatePeer(peer: nil) + if let source = peer.ssrc { + if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source { + strongSelf.currentDominantSpeakerWithVideo = (peerId, source) + strongSelf.call.setFullSizeVideo(peerId: peerId) + strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source)) + } else { + strongSelf.currentDominantSpeakerWithVideo = nil + strongSelf.call.setFullSizeVideo(peerId: nil) + strongSelf.mainVideoContainer?.updatePeer(peer: nil) + } } } default: @@ -827,7 +840,8 @@ public final class VoiceChatController: ViewController { } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - if peer.id == strongSelf.call.myPeerId { + + if peer.id == strongSelf.callState?.myPeerId { return } if let participant = participant { @@ -1050,7 +1064,7 @@ public final class VoiceChatController: ViewController { f(.default) })))*/ - if peer.id != strongSelf.call.myPeerId { + if peer.id != strongSelf.callState?.myPeerId { if let callState = strongSelf.callState, (callState.canManageCall || callState.adminIds.contains(strongSelf.context.account.peerId)) { if callState.adminIds.contains(peer.id) { if let _ = muteState { @@ -1339,7 +1353,7 @@ public final class VoiceChatController: ViewController { } var levels = levels if strongSelf.effectiveMuteState != nil { - levels = levels.filter { $0.0 != strongSelf.call.myPeerId } + levels = levels.filter { $0.0 != strongSelf.callState?.myPeerId } } var maxLevelWithVideo: (PeerId, UInt32, Float)? @@ -1467,6 +1481,16 @@ public final class VoiceChatController: ViewController { strongSelf.call.setShouldBeRecording(callState.recordingStartTimestamp == nil ? true : false) }))) + + if callState.myPeerId != strongSelf.context.account.peerId { + items.append(.action(ContextMenuActionItem(text: "Switch to personal account", textColor: .destructive, icon: { _ in + return nil + }, action: { _, f in + f(.dismissWithoutContent) + + strongSelf.call.reconnect(as: strongSelf.context.account.peerId) + }))) + } } if items.isEmpty { @@ -1733,6 +1757,8 @@ public final class VoiceChatController: ViewController { if case .ended = gestureRecognizer.state { self.hapticFeedback.error() self.actionButton.layer.addShakeAnimation() + + self.call.raiseHand() } return } @@ -1758,7 +1784,9 @@ public final class VoiceChatController: ViewController { self.call.setIsMuted(action: .muted(isPushToTalkActive: false)) } - self.itemInteraction?.updateAudioLevels([(self.call.myPeerId, 0, 0.0, false)], reset: true) + if let callState = self.callState { + self.itemInteraction?.updateAudioLevels([(callState.myPeerId, 0, 0.0, false)], reset: true) + } if let (layout, navigationHeight) = self.validLayout { self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring)) @@ -2508,7 +2536,7 @@ public final class VoiceChatController: ViewController { let memberState: PeerEntry.State var memberMuteState: GroupCallParticipantsContext.Participant.MuteState? - if member.peer.id == self.call.myPeerId { + if member.peer.id == self.callState?.myPeerId { if muteState == nil { memberState = speakingPeers.contains(member.peer.id) ? .speaking : .listening } else { @@ -2522,14 +2550,15 @@ public final class VoiceChatController: ViewController { entries.append(.peer(PeerEntry( peer: member.peer, - myPeerId: self.call.myPeerId, + myPeerId: self.callState?.myPeerId, ssrc: member.ssrc, presence: nil, activityTimestamp: Int32.max - 1 - index, state: memberState, muteState: memberMuteState, canManageCall: self.callState?.canManageCall ?? false, - volume: member.volume + volume: member.volume, + raiseHandStatus: member.raiseHandRating != nil ))) index += 1 } @@ -2542,14 +2571,15 @@ public final class VoiceChatController: ViewController { entries.append(.peer(PeerEntry( peer: peer, - myPeerId: self.call.myPeerId, - ssrc: 0, + myPeerId: self.callState?.myPeerId, + ssrc: nil, presence: nil, activityTimestamp: Int32.max - 1 - index, state: .invited, muteState: nil, canManageCall: false, - volume: nil + volume: nil, + raiseHandStatus: false ))) index += 1 } diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 22a734bc1f..81df9f49dd 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -1260,7 +1260,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo category = .voiceChat } - updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId), category: category), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: activity) + updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId), category: category), peerId: userId.peerId, activity: activity) } case let .updateChannelUserTyping(_, channelId, topMsgId, userId, type): if let date = updatesDate, date + 60 > serverTime { @@ -1275,7 +1275,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo category = .thread(threadId) } - updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: channelPeerId, category: category), peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), activity: activity) + updatedState.addPeerInputActivity(chatPeerId: PeerActivitySpace(peerId: channelPeerId, category: category), peerId: userId.peerId, activity: activity) } case let .updateEncryptedChatTyping(chatId): if let date = updatesDate, date + 60 > serverTime { diff --git a/submodules/TelegramCore/Sources/Download.swift b/submodules/TelegramCore/Sources/Download.swift index db6e9e3307..4a4bd10b30 100644 --- a/submodules/TelegramCore/Sources/Download.swift +++ b/submodules/TelegramCore/Sources/Download.swift @@ -325,7 +325,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate { } } - func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true) -> Signal { + func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true) -> Signal<(Any, Double), (MTRpcError, Double)> { let requestService = self.requestService return Signal { subscriber in let request = MTRequest() @@ -351,9 +351,9 @@ class Download: NSObject, MTRequestMessageServiceDelegate { request.completed = { (boxedResponse, timestamp, error) -> () in if let error = error { - subscriber.putError(error) + subscriber.putError((error, timestamp)) } else { - subscriber.putNext((boxedResponse as! BoxedMessage).body) + subscriber.putNext(((boxedResponse as! BoxedMessage).body, timestamp)) subscriber.putCompletion() } } diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index 672c1453f5..bae18aaf71 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -40,7 +40,7 @@ public struct GroupCallSummary: Equatable { extension GroupCallInfo { init?(_ call: Api.GroupCall) { switch call { - case let .groupCall(_, id, accessHash, participantCount, params, title, recordStartDate, streamDcId, _): + case let .groupCall(_, id, accessHash, participantCount, params, title, streamDcId, recordStartDate, _): var clientParams: String? if let params = params { switch params { @@ -106,8 +106,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int loop: for participant in participants { switch participant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about): - + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -131,7 +130,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int } else if mutedByYou { muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou) } - var jsonParams: String? + let jsonParams: String? = nil /*if let params = params { switch params { case let .dataJSON(data): @@ -143,6 +142,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int ssrc: ssrc, jsonParams: jsonParams, joinTimestamp: date, + raiseHandRating: raiseHandRating, activityTimestamp: activeDate.flatMap(Double.init), activityRank: nil, muteState: muteState, @@ -174,20 +174,16 @@ public func createGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?) - if let joinAs = joinAs { return (callPeer, transaction.getPeer(joinAs).flatMap(apiInputPeer)) } else { - return (callPeer, nil) + return (callPeer, .inputPeerSelf) } } |> castError(CreateGroupCallError.self) |> mapToSignal { (inputPeer, inputJoinAs) -> Signal in - guard let inputPeer = inputPeer else { + guard let inputPeer = inputPeer, let _ = inputJoinAs else { return .fail(.generic) } - var flags: Int32 = 0 - if let _ = inputJoinAs { - flags |= (1 << 0) - } - return account.network.request(Api.functions.phone.createGroupCall(flags: flags, peer: inputPeer, joinAs: inputJoinAs, randomId: Int32.random(in: Int32.min ... Int32.max))) + return account.network.request(Api.functions.phone.createGroupCall(peer: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max))) |> mapError { error -> CreateGroupCallError in if error.errorDescription == "ANONYMOUS_CALLS_DISABLED" { return .anonymousNotAllowed @@ -281,7 +277,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash loop: for participant in participants { switch participant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): let peerId: PeerId switch apiPeerId { @@ -305,7 +301,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash } else if mutedByYou { muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou) } - var jsonParams: String? + let jsonParams: String? = nil /*if let params = params { switch params { case let .dataJSON(data): @@ -317,6 +313,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash ssrc: ssrc, jsonParams: jsonParams, joinTimestamp: date, + raiseHandRating: raiseHandRating, activityTimestamp: activeDate.flatMap(Double.init), activityRank: nil, muteState: muteState, @@ -352,12 +349,17 @@ public enum JoinGroupCallError { } public struct JoinGroupCallResult { + public enum ConnectionMode { + case rtc + case broadcast + } + public var callInfo: GroupCallInfo public var state: GroupCallParticipantsContext.State + public var connectionMode: ConnectionMode } public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String) -> Signal { - return account.postbox.transaction { transaction -> Api.InputPeer? in if let joinAs = joinAs { return transaction.getPeer(joinAs).flatMap(apiInputPeer) @@ -367,16 +369,15 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal } |> castError(JoinGroupCallError.self) |> mapToSignal { inputJoinAs in + guard let inputJoinAs = inputJoinAs else { + return .fail(.generic) + } var flags: Int32 = 0 if preferMuted { flags |= (1 << 0) } - if let _ = inputJoinAs { - flags |= (1 << 1) - } - return account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), joinAs: inputJoinAs, params: .dataJSON(data: joinPayload))) |> mapError { error -> JoinGroupCallError in if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" { @@ -491,7 +492,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal maybeParsedCall = GroupCallInfo(call) switch call { - case let .groupCall(flags, _, _, _, _, title, streamDcId, recordStartDate, _): + case let .groupCall(flags, _, _, _, _, title, _, recordStartDate, _): let isMuted = (flags & (1 << 1)) != 0 let canChange = (flags & (1 << 2)) != 0 state.defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange) @@ -519,7 +520,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal state.adminIds = adminIds switch result { - case let .groupCall(call, _, _, chats, users): + case let .groupCall(call, participants, _, chats, users): guard let _ = GroupCallInfo(call) else { return .fail(.generic) } @@ -543,6 +544,17 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal } } + let connectionMode: JoinGroupCallResult.ConnectionMode + if let clientParams = parsedCall.clientParams, let clientParamsData = clientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: clientParamsData, options: [])) as? [String: Any] { + if let stream = dict["stream"] as? Bool, stream { + connectionMode = .broadcast + } else { + connectionMode = .rtc + } + } else { + connectionMode = .broadcast + } + return account.postbox.transaction { transaction -> JoinGroupCallResult in updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in return updated @@ -551,7 +563,8 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal return JoinGroupCallResult( callInfo: parsedCall, - state: state + state: state, + connectionMode: connectionMode ) } |> castError(JoinGroupCallError.self) @@ -635,6 +648,9 @@ public func checkGroupCall(account: Account, callId: Int64, accessHash: Int64, s return .single(.boolFalse) } |> map { result -> CheckGroupCallResult in + #if DEBUG + //return .restart + #endif switch result { case .boolTrue: return .success @@ -673,9 +689,10 @@ public final class GroupCallParticipantsContext { } public var peer: Peer - public var ssrc: UInt32 + public var ssrc: UInt32? public var jsonParams: String? public var joinTimestamp: Int32 + public var raiseHandRating: Int64? public var activityTimestamp: Double? public var activityRank: Int? public var muteState: MuteState? @@ -683,9 +700,10 @@ public final class GroupCallParticipantsContext { public var about: String? public init( peer: Peer, - ssrc: UInt32, + ssrc: UInt32?, jsonParams: String?, joinTimestamp: Int32, + raiseHandRating: Int64?, activityTimestamp: Double?, activityRank: Int?, muteState: MuteState?, @@ -696,6 +714,7 @@ public final class GroupCallParticipantsContext { self.ssrc = ssrc self.jsonParams = jsonParams self.joinTimestamp = joinTimestamp + self.raiseHandRating = raiseHandRating self.activityTimestamp = activityTimestamp self.activityRank = activityRank self.muteState = muteState @@ -713,6 +732,9 @@ public final class GroupCallParticipantsContext { if lhs.joinTimestamp != rhs.joinTimestamp { return false } + if lhs.raiseHandRating != rhs.raiseHandRating { + return false + } if lhs.activityTimestamp != rhs.activityTimestamp { return false } @@ -752,6 +774,16 @@ public final class GroupCallParticipantsContext { return false } + if let lhsRaiseHandRating = lhs.raiseHandRating, let rhsRaiseHandRating = rhs.raiseHandRating { + if lhsRaiseHandRating != rhsRaiseHandRating { + return lhsRaiseHandRating > rhsRaiseHandRating + } + } else if lhs.raiseHandRating != nil { + return true + } else if rhs.raiseHandRating != nil { + return false + } + if lhs.joinTimestamp != rhs.joinTimestamp { return lhs.joinTimestamp > rhs.joinTimestamp } @@ -822,20 +854,22 @@ public final class GroupCallParticipantsContext { } public var peerId: PeerId - public var ssrc: UInt32 + public var ssrc: UInt32? public var jsonParams: String? public var joinTimestamp: Int32 public var activityTimestamp: Double? + public var raiseHandRating: Int64? public var muteState: Participant.MuteState? public var participationStatusChange: ParticipationStatusChange public var volume: Int32? public var about: String? init( peerId: PeerId, - ssrc: UInt32, + ssrc: UInt32?, jsonParams: String?, joinTimestamp: Int32, activityTimestamp: Double?, + raiseHandRating: Int64?, muteState: Participant.MuteState?, participationStatusChange: ParticipationStatusChange, volume: Int32?, @@ -846,6 +880,7 @@ public final class GroupCallParticipantsContext { self.jsonParams = jsonParams self.joinTimestamp = joinTimestamp self.activityTimestamp = activityTimestamp + self.raiseHandRating = raiseHandRating self.muteState = muteState self.participationStatusChange = participationStatusChange self.volume = volume @@ -874,6 +909,7 @@ public final class GroupCallParticipantsContext { } private let account: Account + private let myPeerId: PeerId private let id: Int64 private let accessHash: Int64 @@ -939,8 +975,9 @@ public final class GroupCallParticipantsContext { private let updateDefaultMuteDisposable = MetaDisposable() private let updateShouldBeRecordingDisposable = MetaDisposable() - public init(account: Account, peerId: PeerId, id: Int64, accessHash: Int64, state: State) { + public init(account: Account, peerId: PeerId, myPeerId: PeerId, id: Int64, accessHash: Int64, state: State) { self.account = account + self.myPeerId = myPeerId self.id = id self.accessHash = accessHash self.stateValue = InternalState(state: state, overlayState: OverlayState()) @@ -1002,14 +1039,6 @@ public final class GroupCallParticipantsContext { if updated { updatedParticipants.sort() - /*for i in 0 ..< updatedParticipants.count { - if updatedParticipants[i].peer.id == strongSelf.account.peerId { - let member = updatedParticipants[i] - updatedParticipants.remove(at: i) - updatedParticipants.insert(member, at: 0) - break - } - }*/ strongSelf.stateValue = InternalState( state: State( @@ -1134,14 +1163,6 @@ public final class GroupCallParticipantsContext { if updated { updatedParticipants.sort() - /*for i in 0 ..< updatedParticipants.count { - if updatedParticipants[i].peer.id == strongSelf.account.peerId { - let member = updatedParticipants[i] - updatedParticipants.remove(at: i) - updatedParticipants.insert(member, at: 0) - break - } - }*/ strongSelf.stateValue = InternalState( state: State( @@ -1167,7 +1188,9 @@ public final class GroupCallParticipantsContext { var existingSsrcs = Set() for participant in self.stateValue.state.participants { - existingSsrcs.insert(participant.ssrc) + if let ssrc = participant.ssrc { + existingSsrcs.insert(ssrc) + } } for ssrc in ssrcs { @@ -1312,6 +1335,7 @@ public final class GroupCallParticipantsContext { ssrc: participantUpdate.ssrc, jsonParams: participantUpdate.jsonParams, joinTimestamp: participantUpdate.joinTimestamp, + raiseHandRating: participantUpdate.raiseHandRating, activityTimestamp: activityTimestamp, activityRank: previousActivityRank, muteState: participantUpdate.muteState, @@ -1406,6 +1430,7 @@ public final class GroupCallParticipantsContext { let account = self.account let id = self.id let accessHash = self.accessHash + let myPeerId = self.myPeerId let signal: Signal = self.account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) @@ -1418,11 +1443,11 @@ public final class GroupCallParticipantsContext { if let volume = volume, volume > 0 { flags |= 1 << 1 } - if let muteState = muteState, (!muteState.canUnmute || peerId == account.peerId || muteState.mutedByYou) { + if let muteState = muteState, (!muteState.canUnmute || peerId == myPeerId || muteState.mutedByYou) { flags |= 1 << 0 } - return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), participant: inputPeer, volume: volume)) + return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), participant: inputPeer, volume: volume, raiseHand: nil)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -1462,6 +1487,18 @@ public final class GroupCallParticipantsContext { })) } + public func raiseHand() { + let flags: Int32 = 1 << 2 + let signal = account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: self.id, accessHash: self.accessHash), participant: .inputPeerSelf, volume: nil, raiseHand: .boolTrue)) + let _ = (signal + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let strongSelf = self else { + return + } + strongSelf.account.stateManager.addUpdates(result) + }) + } + public func updateShouldBeRecording(_ shouldBeRecording: Bool) { var flags: Int32 = 0 if shouldBeRecording { @@ -1528,7 +1565,7 @@ public final class GroupCallParticipantsContext { extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { init(_ apiParticipant: Api.GroupCallParticipant) { switch apiParticipant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -1560,7 +1597,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { participationStatusChange = .none } - var jsonParams: String? + let jsonParams: String? = nil /*if let params = params { switch params { case let .dataJSON(data): @@ -1574,6 +1611,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { jsonParams: jsonParams, joinTimestamp: date, activityTimestamp: activeDate.flatMap(Double.init), + raiseHandRating: raiseHandRating, muteState: muteState, participationStatusChange: participationStatusChange, volume: volume, @@ -1588,7 +1626,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate { var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = [] for participant in participants { switch participant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -1620,7 +1658,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate { participationStatusChange = .none } - var jsonParams: String? + let jsonParams: String? = nil /*if let params = params { switch params { case let .dataJSON(data): @@ -1634,6 +1672,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate { jsonParams: jsonParams, joinTimestamp: date, activityTimestamp: activeDate.flatMap(Double.init), + raiseHandRating: raiseHandRating, muteState: muteState, participationStatusChange: participationStatusChange, volume: volume, @@ -1693,9 +1732,6 @@ public func editGroupCallTitle(account: Account, callId: Int64, accessHash: Int6 } } - - - public func groupCallDisplayAsAvailablePeers(network: Network, postbox: Postbox) -> Signal<[FoundPeer], NoError> { return network.request(Api.functions.channels.getAdminedPublicChannels(flags: 1 << 2)) |> retryRequest @@ -1713,20 +1749,18 @@ public func groupCallDisplayAsAvailablePeers(network: Network, postbox: Postbox) for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { switch chat { - case let .channel(channel): - if let participantsCount = channel.participantsCount { - subscribers[groupOrChannel.id] = participantsCount - } - case let .chat(chat): - subscribers[groupOrChannel.id] = chat.participantsCount + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount): + if let participantsCount = participantsCount { + subscribers[groupOrChannel.id] = participantsCount + } + case let .chat(_, _, _, _, participantsCount, _, _, _, _, _): + subscribers[groupOrChannel.id] = participantsCount default: break } } } - - - + return postbox.transaction { transaction -> [Peer] in updatePeers(transaction: transaction, peers: peers, update: { _, updated in return updated @@ -1768,21 +1802,67 @@ private func mergeAndSortParticipants(current currentParticipants: [GroupCallPar return mergedParticipants } -public func getAudioBroadcastPart(account: Account, callId: Int64, accessHash: Int64, datacenterId: Int?, timestampId: Int32) -> Signal { - return account.network.multiplexedRequestManager.request(to: .main(account.network.datacenterId), consumerId: Int64.random(in: 0 ..< Int64.max), data: Api.functions.upload.getFile(flags: 0, location: .inputGroupCallStream(call: .inputGroupCall(id: callId, accessHash: accessHash), date: timestampId), offset: 0, limit: 128 * 1024), tag: nil, continueInBackground: false, automaticFloodWait: false) +public func getAudioBroadcastDatacenter(account: Account, callId: Int64, accessHash: Int64) -> Signal { + return account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash))) |> map(Optional.init) - |> `catch` { _ -> Signal in + |> `catch` { _ -> Signal in return .single(nil) } - |> map { result -> Data? in + |> map { result -> Int? in guard let result = result else { return nil } switch result { - case let .file(_, _, bytes): - return bytes.makeData() - case .fileCdnRedirect: - return nil + case let .groupCall(call, _, _, _, _): + return GroupCallInfo(call)?.streamDcId.flatMap(Int.init) + } + } +} + +public struct GetAudioBroadcastPartResult { + public enum Status { + case data(Data) + case notReady + case tooOld + case rejoinNeeded + } + + public var status: Status + public var responseTimestamp: Double +} + +public func getAudioBroadcastPart(account: Account, callId: Int64, accessHash: Int64, datacenterId: Int, timestampId: Int32) -> Signal { + return account.network.multiplexedRequestManager.requestWithAdditionalInfo(to: .main(datacenterId), consumerId: Int64.random(in: 0 ..< Int64.max), data: Api.functions.upload.getFile(flags: 0, location: .inputGroupCallStream(call: .inputGroupCall(id: callId, accessHash: accessHash), date: timestampId), offset: 0, limit: 128 * 1024), tag: nil, continueInBackground: false, automaticFloodWait: false) + |> map { result, responseTimestamp -> GetAudioBroadcastPartResult in + switch result { + case let .file(_, _, bytes): + return GetAudioBroadcastPartResult( + status: .data(bytes.makeData()), + responseTimestamp: responseTimestamp + ) + case .fileCdnRedirect: + return GetAudioBroadcastPartResult( + status: .notReady, + responseTimestamp: responseTimestamp + ) + } + } + |> `catch` { error, responseTimestamp -> Signal in + if error.errorDescription == "GROUPCALL_JOIN_MISSING" { + return .single(GetAudioBroadcastPartResult( + status: .rejoinNeeded, + responseTimestamp: responseTimestamp + )) + } else if error.errorDescription.hasPrefix("FLOOD_WAIT") { + return .single(GetAudioBroadcastPartResult( + status: .notReady, + responseTimestamp: responseTimestamp + )) + } else { + return .single(GetAudioBroadcastPartResult( + status: .notReady, + responseTimestamp: responseTimestamp + )) } } } diff --git a/submodules/TelegramCore/Sources/MultipartUpload.swift b/submodules/TelegramCore/Sources/MultipartUpload.swift index 0736fd4940..359f815c11 100644 --- a/submodules/TelegramCore/Sources/MultipartUpload.swift +++ b/submodules/TelegramCore/Sources/MultipartUpload.swift @@ -179,7 +179,7 @@ private final class MultipartUploadManager { self.bigParts = true } else if useLargerParts { self.bigParts = false - self.defaultPartSize = 128 * 1024 + self.defaultPartSize = 256 * 1024 self.bigTotalParts = nil } else { self.bigParts = false @@ -218,7 +218,7 @@ private final class MultipartUploadManager { } else { self.bigParts = false if self.useLargerParts { - self.defaultPartSize = 128 * 1024 + self.defaultPartSize = 256 * 1024 } else { self.defaultPartSize = 16 * 1024 } diff --git a/submodules/TelegramCore/Sources/MultiplexedRequestManager.swift b/submodules/TelegramCore/Sources/MultiplexedRequestManager.swift index 48d0f8e60e..29eef5bcd7 100644 --- a/submodules/TelegramCore/Sources/MultiplexedRequestManager.swift +++ b/submodules/TelegramCore/Sources/MultiplexedRequestManager.swift @@ -24,10 +24,10 @@ private final class RequestData { let continueInBackground: Bool let automaticFloodWait: Bool let deserializeResponse: (Buffer) -> Any? - let completed: (Any) -> Void - let error: (MTRpcError) -> Void + let completed: (Any, Double) -> Void + let error: (MTRpcError, Double) -> Void - init(id: Int32, consumerId: Int64, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any) -> Void, error: @escaping (MTRpcError) -> Void) { + init(id: Int32, consumerId: Int64, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any, Double) -> Void, error: @escaping (MTRpcError, Double) -> Void) { self.id = id self.consumerId = consumerId self.target = target @@ -100,17 +100,17 @@ private final class MultiplexedRequestManagerContext { } } - func request(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, completed: @escaping (Any) -> Void, error: @escaping (MTRpcError) -> Void) -> Disposable { + func request(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, completed: @escaping (Any, Double) -> Void, error: @escaping (MTRpcError, Double) -> Void) -> Disposable { let targetKey = MultiplexedRequestTargetKey(target: target, continueInBackground: continueInBackground) let requestId = self.nextId self.nextId += 1 self.queuedRequests.append(RequestData(id: requestId, consumerId: consumerId, target: target, functionDescription: data.0, payload: data.1, tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, deserializeResponse: { buffer in return data.2(buffer) - }, completed: { result in - completed(result) - }, error: { e in - error(e) + }, completed: { result, timestamp in + completed(result, timestamp) + }, error: { e, timestamp in + error(e, timestamp) })) self.updateState() @@ -180,7 +180,7 @@ private final class MultiplexedRequestManagerContext { let requestId = request.id selectedContext.requests.append(ExecutingRequestData(requestId: requestId, disposable: disposable)) let queue = self.queue - disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse), automaticFloodWait: request.automaticFloodWait).start(next: { [weak self, weak selectedContext] result in + disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse), automaticFloodWait: request.automaticFloodWait).start(next: { [weak self, weak selectedContext] result, timestamp in queue.async { guard let strongSelf = self else { return @@ -193,15 +193,15 @@ private final class MultiplexedRequestManagerContext { } } } - request.completed(result) + request.completed(result, timestamp) strongSelf.updateState() } - }, error: { [weak self, weak selectedContext] error in + }, error: { [weak self, weak selectedContext] error, timestamp in queue.async { guard let strongSelf = self else { return } - request.error(error) + request.error(error, timestamp) if let selectedContext = selectedContext { for i in 0 ..< selectedContext.requests.count { if selectedContext.requests[i].requestId == requestId { @@ -275,18 +275,39 @@ final class MultiplexedRequestManager { self.context.with { context in disposable.set(context.request(to: target, consumerId: consumerId, data: (data.0, data.1, { buffer in return data.2.parse(buffer) - }), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, completed: { result in + }), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, completed: { result, _ in if let result = result as? T { subscriber.putNext(result) subscriber.putCompletion() } else { subscriber.putError(MTRpcError(errorCode: 500, errorDescription: "TL_VERIFICATION_ERROR")) } - }, error: { error in + }, error: { error, _ in subscriber.putError(error) })) } return disposable } } + + func requestWithAdditionalInfo(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true) -> Signal<(T, Double), (MTRpcError, Double)> { + return Signal { subscriber in + let disposable = MetaDisposable() + self.context.with { context in + disposable.set(context.request(to: target, consumerId: consumerId, data: (data.0, data.1, { buffer in + return data.2.parse(buffer) + }), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, completed: { result, timestamp in + if let result = result as? T { + subscriber.putNext((result, timestamp)) + subscriber.putCompletion() + } else { + subscriber.putError((MTRpcError(errorCode: 500, errorDescription: "TL_VERIFICATION_ERROR"), timestamp)) + } + }, error: { error, timestamp in + subscriber.putError((error, timestamp)) + })) + } + return disposable + } + } } diff --git a/submodules/TelegramVoip/Sources/GroupCallContext.swift b/submodules/TelegramVoip/Sources/GroupCallContext.swift index 0d8cb340b6..11a090d1b6 100644 --- a/submodules/TelegramVoip/Sources/GroupCallContext.swift +++ b/submodules/TelegramVoip/Sources/GroupCallContext.swift @@ -32,7 +32,7 @@ private final class ContextQueueImpl: NSObject, OngoingCallThreadLocalContextQue } private protocol BroadcastPartSource: class { - var parts: Signal<[OngoingGroupCallBroadcastPart], NoError> { get } + func requestPart(timestamp: Int32, completion: @escaping (OngoingGroupCallBroadcastPart) -> Void, rejoinNeeded: @escaping () -> Void) -> Disposable } private final class NetworkBroadcastPartSource: BroadcastPartSource { @@ -40,17 +40,9 @@ private final class NetworkBroadcastPartSource: BroadcastPartSource { private let account: Account private let callId: Int64 private let accessHash: Int64 - private let datacenterId: Int? + private var datacenterId: Int? - private let partsPipe = ValuePipe<[OngoingGroupCallBroadcastPart]>() - var parts: Signal<[OngoingGroupCallBroadcastPart], NoError> { - return self.partsPipe.signal() - } - private var timer: SwiftSignalKit.Timer? - private let disposable = MetaDisposable() - - private var nextTimestampId: Int32? init(queue: Queue, account: Account, callId: Int64, accessHash: Int64, datacenterId: Int?) { self.queue = queue @@ -58,44 +50,69 @@ private final class NetworkBroadcastPartSource: BroadcastPartSource { self.callId = callId self.accessHash = accessHash self.datacenterId = datacenterId + } + + func requestPart(timestamp: Int32, completion: @escaping (OngoingGroupCallBroadcastPart) -> Void, rejoinNeeded: @escaping () -> Void) -> Disposable { + let timestampId = timestamp == 0 ? Int32(Date().timeIntervalSince1970) : timestamp - self.check() - } - - deinit { - self.timer?.invalidate() - self.disposable.dispose() - } - - private func check() { - let timestampId: Int32 - if let nextTimestampId = self.nextTimestampId { - timestampId = nextTimestampId + let datacenterId: Signal + if let datacenterIdValue = self.datacenterId { + datacenterId = .single(datacenterIdValue) } else { - timestampId = Int32(Date().timeIntervalSince1970) + datacenterId = getAudioBroadcastDatacenter(account: self.account, callId: self.callId, accessHash: self.accessHash) } - self.disposable.set((getAudioBroadcastPart(account: self.account, callId: self.callId, accessHash: self.accessHash, datacenterId: self.datacenterId, timestampId: timestampId) - |> deliverOn(self.queue)).start(next: { [weak self] data in - guard let strongSelf = self else { + + let account = self.account + let callId = self.callId + let accessHash = self.accessHash + + let queue = self.queue + let signal = datacenterId + |> deliverOn(self.queue) + |> mapToSignal { [weak self] datacenterId -> Signal in + if let datacenterId = datacenterId { + self?.datacenterId = datacenterId + return getAudioBroadcastPart(account: account, callId: callId, accessHash: accessHash, datacenterId: datacenterId, timestampId: timestampId) + |> map(Optional.init) + } else { + return .single(nil) + |> delay(2.0, queue: queue) + } + } + |> deliverOn(self.queue) + + return signal.start(next: { result in + guard let result = result else { + completion(OngoingGroupCallBroadcastPart(timestamp: timestampId, responseTimestamp: Double(timestampId), status: .notReady, oggData: Data())) return } - if let data = data { - var parts: [OngoingGroupCallBroadcastPart] = [] - parts.append(OngoingGroupCallBroadcastPart(timestamp: timestampId, oggData: data)) - - strongSelf.nextTimestampId = timestampId + 1 - - if !parts.isEmpty { - strongSelf.partsPipe.putNext(parts) - } + let part: OngoingGroupCallBroadcastPart + switch result.status { + case let .data(dataValue): + part = OngoingGroupCallBroadcastPart(timestamp: timestampId, responseTimestamp: result.responseTimestamp, status: .success, oggData: dataValue) + case .notReady: + part = OngoingGroupCallBroadcastPart(timestamp: timestampId, responseTimestamp: result.responseTimestamp, status: .notReady, oggData: Data()) + case .tooOld: + part = OngoingGroupCallBroadcastPart(timestamp: timestampId, responseTimestamp: result.responseTimestamp, status: .tooOld, oggData: Data()) + case .rejoinNeeded: + rejoinNeeded() + return } - strongSelf.timer?.invalidate() - strongSelf.timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: false, completion: { - self?.check() - }, queue: strongSelf.queue) - strongSelf.timer?.start() - })) + completion(part) + }) + } +} + +private final class OngoingGroupCallBroadcastPartTaskImpl : NSObject, OngoingGroupCallBroadcastPartTask { + private let disposable: Disposable? + + init(disposable: Disposable?) { + self.disposable = disposable + } + + func cancel() { + self.disposable?.dispose() } } @@ -114,6 +131,12 @@ public final class OngoingGroupCallContext { } } + public enum ConnectionMode { + case none + case rtc + case broadcast + } + public enum NetworkState { case connecting case connected @@ -138,14 +161,20 @@ public final class OngoingGroupCallContext { let videoSources = ValuePromise>(Set(), ignoreRepeated: true) private var broadcastPartsSource: BroadcastPartSource? - private var broadcastPartsDisposable: Disposable? - init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?) { + init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void) { self.queue = queue var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)? var audioLevelsUpdatedImpl: (([NSNumber]) -> Void)? + if let audioStreamData = audioStreamData { + let broadcastPartsSource = NetworkBroadcastPartSource(queue: queue, account: audioStreamData.account, callId: audioStreamData.callId, accessHash: audioStreamData.accessHash, datacenterId: audioStreamData.datacenterId) + self.broadcastPartsSource = broadcastPartsSource + } + + let broadcastPartsSource = self.broadcastPartsSource + let videoSources = self.videoSources self.context = GroupCallThreadLocalContext( queue: ContextQueueImpl(queue: queue), @@ -164,34 +193,17 @@ public final class OngoingGroupCallContext { participantDescriptionsRequired: { ssrcs in participantDescriptionsRequired(Set(ssrcs.map { $0.uint32Value })) }, - externalDecodeOgg: { sourceData in - let tempFile = TempBox.shared.tempFile(fileName: "audio.ogg") - defer { - TempBox.shared.dispose(tempFile) + requestBroadcastPart: { timestamp, completion in + let disposable = MetaDisposable() + + queue.async { + disposable.set(broadcastPartsSource?.requestPart(timestamp: timestamp, completion: completion, rejoinNeeded: { + rejoinNeeded() + })) } - guard let _ = try? sourceData.write(to: URL(fileURLWithPath: tempFile.path), options: .atomic) else { - return nil - } - - var resultData = Data() - - let source = SoftwareAudioSource(path: tempFile.path) - while true { - if let frame = source.readFrame() { - resultData.append(frame) - } else { - break - } - } - - if resultData.isEmpty { - return nil - } else { - return resultData - } - }, - disableIncomingChannels: audioStreamData?.account.testingEnvironment ?? false + return OngoingGroupCallBroadcastPartTaskImpl(disposable: disposable) + } ) let queue = self.queue @@ -242,23 +254,9 @@ public final class OngoingGroupCallContext { strongSelf.joinPayload.set(.single((payload, ssrc))) } }) - - if let audioStreamData = audioStreamData { - let broadcastPartsSource = NetworkBroadcastPartSource(queue: queue, account: audioStreamData.account, callId: audioStreamData.callId, accessHash: audioStreamData.accessHash, datacenterId: audioStreamData.datacenterId) - //let broadcastPartsSource = DemoBroadcastPartSource(queue: queue) - self.broadcastPartsSource = broadcastPartsSource - self.broadcastPartsDisposable = (broadcastPartsSource.parts - |> deliverOn(queue)).start(next: { [weak self] parts in - guard let strongSelf = self else { - return - } - strongSelf.context.add(parts) - }) - } } deinit { - self.broadcastPartsDisposable?.dispose() } func setJoinResponse(payload: String, participants: [(UInt32, String?)]) { @@ -300,6 +298,33 @@ public final class OngoingGroupCallContext { self.context.stop() } + func setConnectionMode(_ connectionMode: ConnectionMode) { + let mappedConnectionMode: OngoingCallConnectionMode + switch connectionMode { + case .none: + mappedConnectionMode = .none + case .rtc: + mappedConnectionMode = .rtc + case .broadcast: + mappedConnectionMode = .broadcast + } + self.context.setConnectionMode(mappedConnectionMode) + + if (mappedConnectionMode != .rtc) { + self.joinPayload.set(.never()) + + let queue = self.queue + self.context.emitJoinPayload({ [weak self] payload, ssrc in + queue.async { + guard let strongSelf = self else { + return + } + strongSelf.joinPayload.set(.single((payload, ssrc))) + } + }) + } + } + func setIsMuted(_ isMuted: Bool) { self.isMuted.set(isMuted) self.context.setIsMuted(isMuted) @@ -475,13 +500,19 @@ public final class OngoingGroupCallContext { } } - public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?) { + public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void) { let queue = self.queue self.impl = QueueLocalObject(queue: queue, generate: { - return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, participantDescriptionsRequired: participantDescriptionsRequired, audioStreamData: audioStreamData) + return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, participantDescriptionsRequired: participantDescriptionsRequired, audioStreamData: audioStreamData, rejoinNeeded: rejoinNeeded) }) } + public func setConnectionMode(_ connectionMode: ConnectionMode) { + self.impl.with { impl in + impl.setConnectionMode(connectionMode) + } + } + public func setIsMuted(_ isMuted: Bool) { self.impl.with { impl in impl.setIsMuted(isMuted) diff --git a/submodules/TgVoipWebrtc/BUILD b/submodules/TgVoipWebrtc/BUILD index 5d0a373fc4..bd281b1b2c 100644 --- a/submodules/TgVoipWebrtc/BUILD +++ b/submodules/TgVoipWebrtc/BUILD @@ -20,6 +20,7 @@ objc_library( "tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*", "tgcalls/tgcalls/platform/darwin/GLVideoViewMac.*", "tgcalls/tgcalls/platform/darwin/ScreenCapturer.*", + "tgcalls/tgcalls/desktop_capturer/**", ]), hdrs = glob([ "PublicHeaders/**/*.h", diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h index 593d37819f..7aeca2c127 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h @@ -165,21 +165,43 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) { @end +@protocol OngoingGroupCallBroadcastPartTask + +- (void)cancel; + +@end + +typedef NS_ENUM(int32_t, OngoingCallConnectionMode) { + OngoingCallConnectionModeNone, + OngoingCallConnectionModeRtc, + OngoingCallConnectionModeBroadcast +}; + +typedef NS_ENUM(int32_t, OngoingGroupCallBroadcastPartStatus) { + OngoingGroupCallBroadcastPartStatusSuccess, + OngoingGroupCallBroadcastPartStatusNotReady, + OngoingGroupCallBroadcastPartStatusTooOld +}; + @interface OngoingGroupCallBroadcastPart : NSObject @property (nonatomic, readonly) int32_t timestamp; +@property (nonatomic, readonly) double responseTimestamp; +@property (nonatomic, readonly) OngoingGroupCallBroadcastPartStatus status; @property (nonatomic, strong, readonly) NSData * _Nonnull oggData; -- (instancetype _Nonnull)initWithTimestamp:(int32_t)timestamp oggData:(NSData * _Nonnull)oggData; +- (instancetype _Nonnull)initWithTimestamp:(int32_t)timestamp responseTimestamp:(double)responseTimestamp status:(OngoingGroupCallBroadcastPartStatus)status oggData:(NSData * _Nonnull)oggData; @end @interface GroupCallThreadLocalContext : NSObject -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired externalDecodeOgg:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull))externalDecodeOgg disableIncomingChannels:(bool)disableIncomingChannels; +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id _Nonnull (^ _Nonnull)(int32_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart; - (void)stop; +- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode; + - (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion; - (void)setJoinResponsePayload:(NSString * _Nonnull)payload participants:(NSArray * _Nonnull)participants; - (void)removeSsrcs:(NSArray * _Nonnull)ssrcs; @@ -195,8 +217,6 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) { - (void)switchAudioInput:(NSString * _Nonnull)deviceId; - (void)makeIncomingVideoViewWithSsrc:(uint32_t)ssrc completion:(void (^_Nonnull)(UIView * _Nullable))completion; -- (void)addBroadcastParts:(NSArray * _Nonnull)parts; - @end #endif diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 17dd56b123..7cd580403b 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -819,6 +819,26 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; @end +namespace { + +class BroadcastPartTaskImpl : public tgcalls::BroadcastPartTask { +public: + BroadcastPartTaskImpl(id task) { + _task = task; + } + + virtual ~BroadcastPartTaskImpl() { + } + + virtual void cancel() override { + [_task cancel]; + } + +private: + id _task; +}; + +} @interface GroupCallThreadLocalContext () { id _queue; @@ -833,7 +853,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; @implementation GroupCallThreadLocalContext -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired externalDecodeOgg:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull))externalDecodeOgg disableIncomingChannels:(bool)disableIncomingChannels { +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id _Nonnull (^ _Nonnull)(int32_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart { self = [super init]; if (self != nil) { _queue = queue; @@ -841,19 +861,6 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; _networkStateUpdated = [networkStateUpdated copy]; _videoCapturer = videoCapturer; - std::function &, std::vector const &)> externalDecodeOggFunction; - if (externalDecodeOgg) { - externalDecodeOggFunction = [externalDecodeOgg](std::vector &outPcm, std::vector sourceData) { - @autoreleasepool { - NSData *result = externalDecodeOgg([NSData dataWithBytes:sourceData.data() length:sourceData.size()]); - if (result) { - outPcm.resize(result.length); - [result getBytes:outPcm.data() length:result.length]; - } - } - }; - } - __weak GroupCallThreadLocalContext *weakSelf = self; _instance.reset(new tgcalls::GroupInstanceCustomImpl((tgcalls::GroupInstanceDescriptor){ .networkStateUpdated = [weakSelf, queue, networkStateUpdated](bool isConnected) { @@ -891,8 +898,41 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } participantDescriptionsRequired(mappedSources); }, - .externalDecodeOgg = externalDecodeOggFunction, - .disableIncomingChannels = disableIncomingChannels + .requestBroadcastPart = [requestBroadcastPart](int32_t timestamp, std::function completion) -> std::shared_ptr { + id task = requestBroadcastPart(timestamp, ^(OngoingGroupCallBroadcastPart * _Nullable part) { + tgcalls::BroadcastPart parsedPart; + parsedPart.timestamp = part.timestamp; + + parsedPart.responseTimestamp = part.responseTimestamp; + + tgcalls::BroadcastPart::Status mappedStatus; + switch (part.status) { + case OngoingGroupCallBroadcastPartStatusSuccess: { + mappedStatus = tgcalls::BroadcastPart::Status::Success; + break; + } + case OngoingGroupCallBroadcastPartStatusNotReady: { + mappedStatus = tgcalls::BroadcastPart::Status::NotReady; + break; + } + case OngoingGroupCallBroadcastPartStatusTooOld: { + mappedStatus = tgcalls::BroadcastPart::Status::TooOld; + break; + } + default: { + mappedStatus = tgcalls::BroadcastPart::Status::NotReady; + break; + } + } + parsedPart.status = mappedStatus; + + parsedPart.oggData.resize(part.oggData.length); + [part.oggData getBytes:parsedPart.oggData.data() length:part.oggData.length]; + + completion(std::move(parsedPart)); + }); + return std::make_shared(task); + } })); } return self; @@ -1001,6 +1041,31 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn completion(string, payload.ssrc); } +- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode { + if (_instance) { + tgcalls::GroupConnectionMode mappedConnectionMode; + switch (connectionMode) { + case OngoingCallConnectionModeNone: { + mappedConnectionMode = tgcalls::GroupConnectionMode::GroupConnectionModeNone; + break; + } + case OngoingCallConnectionModeRtc: { + mappedConnectionMode = tgcalls::GroupConnectionMode::GroupConnectionModeRtc; + break; + } + case OngoingCallConnectionModeBroadcast: { + mappedConnectionMode = tgcalls::GroupConnectionMode::GroupConnectionModeBroadcast; + break; + } + default: { + mappedConnectionMode = tgcalls::GroupConnectionMode::GroupConnectionModeNone; + break; + } + } + _instance->setConnectionMode(mappedConnectionMode); + } +} + - (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion { if (_instance) { _instance->emitJoinPayload([completion](tgcalls::GroupJoinPayload payload) { @@ -1413,23 +1478,6 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn } } -- (void)addBroadcastParts:(NSArray * _Nonnull)parts { - if (!_instance) { - return; - } - - std::vector parsedParts; - for (OngoingGroupCallBroadcastPart *part in parts) { - tgcalls::BroadcastPart parsedPart; - parsedPart.timestamp = part.timestamp; - parsedPart.oggData.resize(part.oggData.length); - [part.oggData getBytes:parsedPart.oggData.data() length:part.oggData.length]; - - parsedParts.push_back(std::move(parsedPart)); - } - ((tgcalls::GroupInstanceCustomImpl *)(_instance.get()))->addBroadcastParts(std::move(parsedParts)); -} - @end @implementation OngoingGroupCallParticipantDescription @@ -1447,10 +1495,12 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn @implementation OngoingGroupCallBroadcastPart -- (instancetype _Nonnull)initWithTimestamp:(int32_t)timestamp oggData:(NSData * _Nonnull)oggData { +- (instancetype _Nonnull)initWithTimestamp:(int32_t)timestamp responseTimestamp:(double)responseTimestamp status:(OngoingGroupCallBroadcastPartStatus)status oggData:(NSData * _Nonnull)oggData { self = [super init]; if (self != nil) { _timestamp = timestamp; + _responseTimestamp = responseTimestamp; + _status = status; _oggData = oggData; } return self; diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 34acd7798a..824bc1722a 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 34acd7798acbef6f14c15338d6ddb55bb83f1d05 +Subproject commit 824bc1722ae504d469378939f231ee3c3f2c9ce5