[WIP] Voice chat broadcasts

This commit is contained in:
Ali 2021-03-05 19:59:36 +04:00
parent 21df10ffec
commit 3f7eeb944b
16 changed files with 696 additions and 341 deletions

View File

@ -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<PeerId>
@ -180,6 +181,7 @@ public struct PresentationGroupCallState: Equatable {
public var recordingStartTimestamp: Int32?
public init(
myPeerId: PeerId,
networkState: NetworkState,
canManageCall: Bool,
adminIds: Set<PeerId>,
@ -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<PresentationGroupCallMemberEvent, NoError> { get }
func reconnect(as peerId: PeerId)
func leave(terminateIfPossible: Bool) -> Signal<Bool, NoError>
func toggleIsMuted()
func setIsMuted(action: PresentationGroupCallMuteAction)
func raiseHand()
func requestVideo()
func disableVideo()
func updateDefaultParticipantsAreMuted(isMuted: Bool)

View File

@ -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) }

View File

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

View File

@ -7595,14 +7595,12 @@ public extension Api {
})
}
public static func createGroupCall(flags: Int32, peer: Api.InputPeer, joinAs: Api.InputPeer?, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
public static func createGroupCall(peer: Api.InputPeer, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
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<Api.Updates>) {
public static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
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<Api.phone.GroupParticipants>) {
public static func getGroupParticipants(call: Api.InputGroupCall, ids: [Api.InputPeer], sources: [Int32], offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.GroupParticipants>) {
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<Api.Updates>) {
public static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, volume: Int32?, raiseHand: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
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() {

View File

@ -19,7 +19,9 @@ import DeviceProximity
private extension GroupCallParticipantsContext.Participant {
var allSsrcs: Set<UInt32> {
var participantSsrcs = Set<UInt32>()
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<Bool>(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>(PresentationGroupCallState.initialValue)
private let statePromise: ValuePromise<PresentationGroupCallState>
public var state: Signal<PresentationGroupCallState, NoError> {
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<Bool, NoError> {
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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -325,7 +325,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
}
}
func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true) -> Signal<Any, MTRpcError> {
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()
}
}

View File

@ -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<GroupCallInfo, CreateGroupCallError> 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<JoinGroupCallResult, JoinGroupCallError> {
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<UInt32>()
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<Api.Updates?, NoError> = 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<Api.Updates?, NoError> 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<Data?, NoError> {
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<Int?, NoError> {
return account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.upload.File?, NoError> in
|> `catch` { _ -> Signal<Api.phone.GroupCall?, NoError> 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<GetAudioBroadcastPartResult, NoError> {
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<GetAudioBroadcastPartResult, NoError> 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
))
}
}
}

View File

@ -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
}

View File

@ -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<T>(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), 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
}
}
}

View File

@ -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<Int?, NoError>
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<GetAudioBroadcastPartResult?, NoError> 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<UInt32>>(Set(), ignoreRepeated: true)
private var broadcastPartsSource: BroadcastPartSource?
private var broadcastPartsDisposable: Disposable?
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> Void, audioStreamData: AudioStreamData?) {
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> 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<UInt32>) -> Void, audioStreamData: AudioStreamData?) {
public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> 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)

View File

@ -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",

View File

@ -165,21 +165,43 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) {
@end
@protocol OngoingGroupCallBroadcastPartTask <NSObject>
- (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<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired externalDecodeOgg:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull))externalDecodeOgg disableIncomingChannels:(bool)disableIncomingChannels;
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id<OngoingGroupCallBroadcastPartTask> _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<OngoingGroupCallParticipantDescription *> * _Nonnull)participants;
- (void)removeSsrcs:(NSArray<NSNumber *> * _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<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
- (void)addBroadcastParts:(NSArray<OngoingGroupCallBroadcastPart *> * _Nonnull)parts;
@end
#endif

View File

@ -819,6 +819,26 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
@end
namespace {
class BroadcastPartTaskImpl : public tgcalls::BroadcastPartTask {
public:
BroadcastPartTaskImpl(id<OngoingGroupCallBroadcastPartTask> task) {
_task = task;
}
virtual ~BroadcastPartTaskImpl() {
}
virtual void cancel() override {
[_task cancel];
}
private:
id<OngoingGroupCallBroadcastPartTask> _task;
};
}
@interface GroupCallThreadLocalContext () {
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
@ -833,7 +853,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
@implementation GroupCallThreadLocalContext
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired externalDecodeOgg:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull))externalDecodeOgg disableIncomingChannels:(bool)disableIncomingChannels {
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id<OngoingGroupCallBroadcastPartTask> _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<void(std::vector<uint8_t> &, std::vector<uint8_t> const &)> externalDecodeOggFunction;
if (externalDecodeOgg) {
externalDecodeOggFunction = [externalDecodeOgg](std::vector<uint8_t> &outPcm, std::vector<uint8_t> 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<void(tgcalls::BroadcastPart &&)> completion) -> std::shared_ptr<tgcalls::BroadcastPartTask> {
id<OngoingGroupCallBroadcastPartTask> 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<BroadcastPartTaskImpl>(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<OngoingGroupCallBroadcastPart *> * _Nonnull)parts {
if (!_instance) {
return;
}
std::vector<tgcalls::BroadcastPart> 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;

@ -1 +1 @@
Subproject commit 34acd7798acbef6f14c15338d6ddb55bb83f1d05
Subproject commit 824bc1722ae504d469378939f231ee3c3f2c9ce5