[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 case muted
} }
public var myPeerId: PeerId
public var networkState: NetworkState public var networkState: NetworkState
public var canManageCall: Bool public var canManageCall: Bool
public var adminIds: Set<PeerId> public var adminIds: Set<PeerId>
@ -180,6 +181,7 @@ public struct PresentationGroupCallState: Equatable {
public var recordingStartTimestamp: Int32? public var recordingStartTimestamp: Int32?
public init( public init(
myPeerId: PeerId,
networkState: NetworkState, networkState: NetworkState,
canManageCall: Bool, canManageCall: Bool,
adminIds: Set<PeerId>, adminIds: Set<PeerId>,
@ -187,6 +189,7 @@ public struct PresentationGroupCallState: Equatable {
defaultParticipantMuteState: DefaultParticipantMuteState?, defaultParticipantMuteState: DefaultParticipantMuteState?,
recordingStartTimestamp: Int32? recordingStartTimestamp: Int32?
) { ) {
self.myPeerId = myPeerId
self.networkState = networkState self.networkState = networkState
self.canManageCall = canManageCall self.canManageCall = canManageCall
self.adminIds = adminIds self.adminIds = adminIds
@ -282,7 +285,6 @@ public protocol PresentationGroupCall: class {
var accountContext: AccountContext { get } var accountContext: AccountContext { get }
var internalId: CallSessionInternalId { get } var internalId: CallSessionInternalId { get }
var peerId: PeerId { get } var peerId: PeerId { get }
var myPeerId: PeerId { get }
var isVideo: Bool { get } var isVideo: Bool { get }
@ -298,10 +300,12 @@ public protocol PresentationGroupCall: class {
var memberEvents: Signal<PresentationGroupCallMemberEvent, NoError> { get } var memberEvents: Signal<PresentationGroupCallMemberEvent, NoError> { get }
func reconnect(as peerId: PeerId)
func leave(terminateIfPossible: Bool) -> Signal<Bool, NoError> func leave(terminateIfPossible: Bool) -> Signal<Bool, NoError>
func toggleIsMuted() func toggleIsMuted()
func setIsMuted(action: PresentationGroupCallMuteAction) func setIsMuted(action: PresentationGroupCallMuteAction)
func raiseHand()
func requestVideo() func requestVideo()
func disableVideo() func disableVideo()
func updateDefaultParticipantsAreMuted(isMuted: Bool) 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[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) }
dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) } dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) }
dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($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[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) } dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($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[1318109142] = { return Api.Update.parse_updateMessageID($0) }
dict[-1576161051] = { return Api.Update.parse_updateDeleteMessages($0) } dict[-1576161051] = { return Api.Update.parse_updateDeleteMessages($0) }
dict[1548249383] = { return Api.Update.parse_updateUserTyping($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[125178264] = { return Api.Update.parse_updateChatParticipants($0) }
dict[469489699] = { return Api.Update.parse_updateUserStatus($0) } dict[469489699] = { return Api.Update.parse_updateUserStatus($0) }
dict[-1489818765] = { return Api.Update.parse_updateUserName($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[482860628] = { return Api.Update.parse_updateReadChannelDiscussionInbox($0) }
dict[1178116716] = { return Api.Update.parse_updateReadChannelDiscussionOutbox($0) } dict[1178116716] = { return Api.Update.parse_updateReadChannelDiscussionOutbox($0) }
dict[610945826] = { return Api.Update.parse_updatePeerBlocked($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[-309990731] = { return Api.Update.parse_updatePinnedMessages($0) }
dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) } dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) }
dict[321954198] = { return Api.Update.parse_updateChat($0) } dict[321954198] = { return Api.Update.parse_updateChat($0) }

View File

@ -3604,13 +3604,13 @@ public extension Api {
} }
public enum GroupCallParticipant: TypeConstructorDescription { 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) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { 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 { if boxed {
buffer.appendInt32(-763544865) buffer.appendInt32(430815881)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true) peer.serialize(buffer, true)
@ -3619,14 +3619,15 @@ public extension Api {
serializeInt32(source, buffer: buffer, boxed: false) serializeInt32(source, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 7) != 0 {serializeInt32(volume!, 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 << 11) != 0 {serializeString(about!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 13) != 0 {serializeInt64(raiseHandRating!, buffer: buffer, boxed: false)}
break break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { 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):
return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about)]) 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() } if Int(_1!) & Int(1 << 7) != 0 {_6 = reader.readInt32() }
var _7: String? var _7: String?
if Int(_1!) & Int(1 << 11) != 0 {_7 = parseString(reader) } 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 _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -3654,8 +3657,9 @@ public extension Api {
let _c5 = _5 != nil let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7) 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 { else {
return nil return nil
@ -4586,7 +4590,7 @@ public extension Api {
case updateMessageID(id: Int32, randomId: Int64) case updateMessageID(id: Int32, randomId: Int64)
case updateDeleteMessages(messages: [Int32], pts: Int32, ptsCount: Int32) case updateDeleteMessages(messages: [Int32], pts: Int32, ptsCount: Int32)
case updateUserTyping(userId: Int32, action: Api.SendMessageAction) 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 updateChatParticipants(participants: Api.ChatParticipants)
case updateUserStatus(userId: Int32, status: Api.UserStatus) case updateUserStatus(userId: Int32, status: Api.UserStatus)
case updateUserName(userId: Int32, firstName: String, lastName: String, username: String) 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 updateReadChannelDiscussionInbox(flags: Int32, channelId: Int32, topMsgId: Int32, readMaxId: Int32, broadcastId: Int32?, broadcastPost: Int32?)
case updateReadChannelDiscussionOutbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32) case updateReadChannelDiscussionOutbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32)
case updatePeerBlocked(peerId: Api.Peer, blocked: Api.Bool) 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 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 updatePinnedChannelMessages(flags: Int32, channelId: Int32, messages: [Int32], pts: Int32, ptsCount: Int32)
case updateChat(chatId: Int32) case updateChat(chatId: Int32)
@ -4710,12 +4714,12 @@ public extension Api {
serializeInt32(userId, buffer: buffer, boxed: false) serializeInt32(userId, buffer: buffer, boxed: false)
action.serialize(buffer, true) action.serialize(buffer, true)
break break
case .updateChatUserTyping(let chatId, let userId, let action): case .updateChatUserTyping(let chatId, let fromId, let action):
if boxed { if boxed {
buffer.appendInt32(-1704596961) buffer.appendInt32(-2033525908)
} }
serializeInt32(chatId, buffer: buffer, boxed: false) serializeInt32(chatId, buffer: buffer, boxed: false)
serializeInt32(userId, buffer: buffer, boxed: false) fromId.serialize(buffer, true)
action.serialize(buffer, true) action.serialize(buffer, true)
break break
case .updateChatParticipants(let participants): case .updateChatParticipants(let participants):
@ -5359,14 +5363,14 @@ public extension Api {
peerId.serialize(buffer, true) peerId.serialize(buffer, true)
blocked.serialize(buffer, true) blocked.serialize(buffer, true)
break 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 { if boxed {
buffer.appendInt32(-13975905) buffer.appendInt32(1796675352)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(channelId, buffer: buffer, boxed: false) serializeInt32(channelId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, 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) action.serialize(buffer, true)
break break
case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): 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)]) return ("updateDeleteMessages", [("messages", messages), ("pts", pts), ("ptsCount", ptsCount)])
case .updateUserTyping(let userId, let action): case .updateUserTyping(let userId, let action):
return ("updateUserTyping", [("userId", userId), ("action", action)]) return ("updateUserTyping", [("userId", userId), ("action", action)])
case .updateChatUserTyping(let chatId, let userId, let action): case .updateChatUserTyping(let chatId, let fromId, let action):
return ("updateChatUserTyping", [("chatId", chatId), ("userId", userId), ("action", action)]) return ("updateChatUserTyping", [("chatId", chatId), ("fromId", fromId), ("action", action)])
case .updateChatParticipants(let participants): case .updateChatParticipants(let participants):
return ("updateChatParticipants", [("participants", participants)]) return ("updateChatParticipants", [("participants", participants)])
case .updateUserStatus(let userId, let status): case .updateUserStatus(let userId, let status):
@ -5634,8 +5638,8 @@ public extension Api {
return ("updateReadChannelDiscussionOutbox", [("channelId", channelId), ("topMsgId", topMsgId), ("readMaxId", readMaxId)]) return ("updateReadChannelDiscussionOutbox", [("channelId", channelId), ("topMsgId", topMsgId), ("readMaxId", readMaxId)])
case .updatePeerBlocked(let peerId, let blocked): case .updatePeerBlocked(let peerId, let blocked):
return ("updatePeerBlocked", [("peerId", peerId), ("blocked", blocked)]) return ("updatePeerBlocked", [("peerId", peerId), ("blocked", blocked)])
case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action): case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action):
return ("updateChannelUserTyping", [("flags", flags), ("channelId", channelId), ("topMsgId", topMsgId), ("userId", userId), ("action", 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): case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount):
return ("updatePinnedMessages", [("flags", flags), ("peer", peer), ("messages", messages), ("pts", pts), ("ptsCount", 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): 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? { public static func parse_updateChatUserTyping(_ reader: BufferReader) -> Update? {
var _1: Int32? var _1: Int32?
_1 = reader.readInt32() _1 = reader.readInt32()
var _2: Int32? var _2: Api.Peer?
_2 = reader.readInt32() if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _3: Api.SendMessageAction? var _3: Api.SendMessageAction?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.SendMessageAction _3 = Api.parse(reader, signature: signature) as? Api.SendMessageAction
@ -5738,7 +5744,7 @@ public extension Api {
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
if _c1 && _c2 && _c3 { 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 { else {
return nil return nil
@ -7028,8 +7034,10 @@ public extension Api {
_2 = reader.readInt32() _2 = reader.readInt32()
var _3: Int32? var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() } if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
var _4: Int32? var _4: Api.Peer?
_4 = reader.readInt32() if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _5: Api.SendMessageAction? var _5: Api.SendMessageAction?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.SendMessageAction _5 = Api.parse(reader, signature: signature) as? Api.SendMessageAction
@ -7040,7 +7048,7 @@ public extension Api {
let _c4 = _4 != nil let _c4 = _4 != nil
let _c5 = _5 != nil let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 { 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 { else {
return nil 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() let buffer = Buffer()
buffer.appendInt32(534090322) buffer.appendInt32(-1120031776)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true) peer.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {joinAs!.serialize(buffer, true)}
serializeInt32(randomId, buffer: buffer, boxed: false) 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) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { 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() let buffer = Buffer()
buffer.appendInt32(780232376) buffer.appendInt32(909354416)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true) call.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {joinAs!.serialize(buffer, true)} joinAs.serialize(buffer, true)
params.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 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) 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() let buffer = Buffer()
buffer.appendInt32(-906898811) buffer.appendInt32(-984033109)
call.serialize(buffer, true) call.serialize(buffer, true)
buffer.appendInt32(481674261) buffer.appendInt32(481674261)
buffer.appendInt32(Int32(ids.count)) buffer.appendInt32(Int32(ids.count))
for item in ids { for item in ids {
serializeInt32(item, buffer: buffer, boxed: false) item.serialize(buffer, true)
} }
buffer.appendInt32(481674261) buffer.appendInt32(481674261)
buffer.appendInt32(Int32(sources.count)) 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() let buffer = Buffer()
buffer.appendInt32(1192486819) buffer.appendInt32(-646583424)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true) call.serialize(buffer, true)
participant.serialize(buffer, true) participant.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} 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) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -19,7 +19,9 @@ import DeviceProximity
private extension GroupCallParticipantsContext.Participant { private extension GroupCallParticipantsContext.Participant {
var allSsrcs: Set<UInt32> { var allSsrcs: Set<UInt32> {
var participantSsrcs = 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 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] { if let groups = json["ssrc-groups"] as? [Any] {
for group in groups { for group in groups {
@ -93,6 +95,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
let context = GroupCallParticipantsContext( let context = GroupCallParticipantsContext(
account: account, account: account,
peerId: peerId, peerId: peerId,
myPeerId: account.peerId,
id: call.id, id: call.id,
accessHash: call.accessHash, accessHash: call.accessHash,
state: state state: state
@ -191,8 +194,9 @@ public final class AccountGroupCallContextCacheImpl: AccountGroupCallContextCach
} }
private extension PresentationGroupCallState { private extension PresentationGroupCallState {
static var initialValue: PresentationGroupCallState { static func initialValue(myPeerId: PeerId) -> PresentationGroupCallState {
return PresentationGroupCallState( return PresentationGroupCallState(
myPeerId: myPeerId,
networkState: .connecting, networkState: .connecting,
canManageCall: false, canManageCall: false,
adminIds: Set(), adminIds: Set(),
@ -207,7 +211,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private enum InternalState { private enum InternalState {
case requesting case requesting
case active(GroupCallInfo) 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? { var callInfo: GroupCallInfo? {
switch self { switch self {
@ -215,7 +219,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return nil return nil
case let .active(info): case let .active(info):
return info return info
case let .estabilished(info, _, _, _): case let .established(info, _, _, _, _):
return info return info
} }
} }
@ -337,8 +341,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var initialCall: CachedChannelData.ActiveCall? private var initialCall: CachedChannelData.ActiveCall?
public let internalId: CallSessionInternalId public let internalId: CallSessionInternalId
public let peerId: PeerId public let peerId: PeerId
private let joinAsPeerId: PeerId private var joinAsPeerId: PeerId
public let myPeerId: PeerId
public let peer: Peer? public let peer: Peer?
public private(set) var isVideo: Bool public private(set) var isVideo: Bool
@ -424,14 +427,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private let wasRemoved = Promise<Bool>(false) private let wasRemoved = Promise<Bool>(false)
private var stateValue = PresentationGroupCallState.initialValue { private var stateValue: PresentationGroupCallState {
didSet { didSet {
if self.stateValue != oldValue { if self.stateValue != oldValue {
self.statePromise.set(self.stateValue) self.statePromise.set(self.stateValue)
} }
} }
} }
private let statePromise = ValuePromise<PresentationGroupCallState>(PresentationGroupCallState.initialValue) private let statePromise: ValuePromise<PresentationGroupCallState>
public var state: Signal<PresentationGroupCallState, NoError> { public var state: Signal<PresentationGroupCallState, NoError> {
return self.statePromise.get() return self.statePromise.get()
} }
@ -522,7 +525,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.peerId = peerId self.peerId = peerId
self.peer = peer self.peer = peer
self.joinAsPeerId = joinAsPeerId ?? accountContext.account.peerId 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) self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
@ -630,7 +635,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if case let .estabilished(callInfo, _, _, _) = strongSelf.internalState { if case let .established(callInfo, _, _, _, _) = strongSelf.internalState {
var addedParticipants: [(UInt32, String?)] = [] var addedParticipants: [(UInt32, String?)] = []
var removedSsrc: [UInt32] = [] var removedSsrc: [UInt32] = []
for (callId, update) in updates { for (callId, update) in updates {
@ -639,21 +644,38 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
case let .state(update): case let .state(update):
for participantUpdate in update.participantUpdates { for participantUpdate in update.participantUpdates {
if case .left = participantUpdate.participationStatusChange { if case .left = participantUpdate.participationStatusChange {
removedSsrc.append(participantUpdate.ssrc) if let ssrc = participantUpdate.ssrc {
removedSsrc.append(ssrc)
}
if participantUpdate.peerId == strongSelf.joinAsPeerId { 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)) strongSelf._canBeRemoved.set(.single(true))
} }
} }
} else if participantUpdate.peerId == strongSelf.joinAsPeerId { } else if participantUpdate.peerId == strongSelf.joinAsPeerId {
if case let .estabilished(_, _, ssrc, _) = strongSelf.internalState, ssrc != participantUpdate.ssrc { if case let .established(_, connectionMode, _, ssrc, _) = strongSelf.internalState {
if ssrc != participantUpdate.ssrc {
strongSelf._canBeRemoved.set(.single(true)) 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 { } else if case .joined = participantUpdate.participationStatusChange {
addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams)) if let ssrc = participantUpdate.ssrc {
} else if strongSelf.ssrcMapping[participantUpdate.ssrc] == nil { addedParticipants.append((ssrc, participantUpdate.jsonParams))
addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams)) }
} else if let ssrc = participantUpdate.ssrc, strongSelf.ssrcMapping[ssrc] == nil {
addedParticipants.append((ssrc, participantUpdate.jsonParams))
} }
} }
case let .call(isTerminated, _, _, _): case let .call(isTerminated, _, _, _):
@ -723,9 +745,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if let myPeer = myPeer { if let myPeer = myPeer {
participants.append(GroupCallParticipantsContext.Participant( participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer, peer: myPeer,
ssrc: 0, ssrc: nil,
jsonParams: nil, jsonParams: nil,
joinTimestamp: strongSelf.temporaryJoinTimestamp, joinTimestamp: strongSelf.temporaryJoinTimestamp,
raiseHandRating: nil,
activityTimestamp: nil, activityTimestamp: nil,
activityRank: nil, activityRank: nil,
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
@ -848,14 +871,27 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
break break
default: default:
if case let .active(callInfo) = internalState { if case let .active(callInfo) = internalState {
let callContext = OngoingGroupCallContext(video: self.videoCapturer, participantDescriptionsRequired: { [weak self] ssrcs in let callContext: OngoingGroupCallContext
if let current = self.callContext {
callContext = current
} else {
callContext = OngoingGroupCallContext(video: self.videoCapturer, participantDescriptionsRequired: { [weak self] ssrcs in
Queue.mainQueue().async { Queue.mainQueue().async {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.maybeRequestParticipants(ssrcs: ssrcs) strongSelf.maybeRequestParticipants(ssrcs: ssrcs)
} }
}, audioStreamData: OngoingGroupCallContext.AudioStreamData(account: self.accountContext.account, callId: callInfo.id, accessHash: callInfo.accessHash, datacenterId: callInfo.streamDcId.flatMap(Int.init))) }, 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 self.incomingVideoSourcePromise.set(callContext.videoSources
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { [weak self] sources -> [PeerId: UInt32] in |> map { [weak self] sources -> [PeerId: UInt32] in
@ -871,6 +907,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return result return result
}) })
self.callContext = callContext self.callContext = callContext
}
self.joinDisposable.set((callContext.joinPayload self.joinDisposable.set((callContext.joinPayload
|> distinctUntilChanged(isEqual: { lhs, rhs in |> distinctUntilChanged(isEqual: { lhs, rhs in
if lhs.0 != rhs.0 { if lhs.0 != rhs.0 {
@ -900,14 +937,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
if let clientParams = joinCallResult.callInfo.clientParams { if let clientParams = joinCallResult.callInfo.clientParams {
strongSelf.ssrcMapping.removeAll() strongSelf.ssrcMapping.removeAll()
var addedParticipants: [(UInt32, String?)] = [] let addedParticipants: [(UInt32, String?)] = []
for participant in joinCallResult.state.participants { for participant in joinCallResult.state.participants {
strongSelf.ssrcMapping[participant.ssrc] = participant.peer.id if let ssrc = participant.ssrc {
strongSelf.ssrcMapping[ssrc] = participant.peer.id
//addedParticipants.append((participant.ssrc, participant.jsonParams)) //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 }, error: { error in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -1028,10 +1074,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
switch previousInternalState { switch previousInternalState {
case .estabilished: case .established:
break break
default: default:
if case let .estabilished(callInfo, clientParams, _, initialState) = internalState { if case let .established(callInfo, _, _, _, initialState) = internalState {
self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo))) self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo)))
self.stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId) self.stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId)
@ -1072,17 +1118,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let participantsContext = GroupCallParticipantsContext( let participantsContext = GroupCallParticipantsContext(
account: self.accountContext.account, account: self.accountContext.account,
peerId: self.peerId, peerId: self.peerId,
myPeerId: self.joinAsPeerId,
id: callInfo.id, id: callInfo.id,
accessHash: callInfo.accessHash, accessHash: callInfo.accessHash,
state: initialState state: initialState
) )
self.participantsContext = participantsContext 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(), self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
participantsContext.state, participantsContext.state,
participantsContext.activeSpeakers, participantsContext.activeSpeakers,
self.speakingParticipantsContext.get(), self.speakingParticipantsContext.get(),
adminIds adminIds,
).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds in myPeer
).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeer in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -1120,14 +1172,35 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
var updatedInvitedPeers = strongSelf.invitedPeersValue var updatedInvitedPeers = strongSelf.invitedPeersValue
var didUpdateInvitedPeers = false 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) members.participants.append(participant)
if topParticipants.count < 3 { if topParticipants.count < 3 {
topParticipants.append(participant) 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 participant.peer.id == strongSelf.joinAsPeerId {
if let muteState = participant.muteState { if let muteState = participant.muteState {
@ -1152,10 +1225,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
strongSelf.callContext?.setIsMuted(true) strongSelf.callContext?.setIsMuted(true)
} }
} else { } else {
if let ssrc = participant.ssrc {
if let volume = participant.volume { if let volume = participant.volume {
strongSelf.callContext?.setVolume(ssrc: participant.ssrc, volume: Double(volume) / 10000.0) strongSelf.callContext?.setVolume(ssrc: ssrc, volume: Double(volume) / 10000.0)
} else if participant.muteState?.mutedByYou == true { } else if participant.muteState?.mutedByYou == true {
strongSelf.callContext?.setVolume(ssrc: participant.ssrc, volume: 0.0) strongSelf.callContext?.setVolume(ssrc: ssrc, volume: 0.0)
}
} }
} }
@ -1230,7 +1305,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
missingSsrcs.subtract(participantSsrcs) missingSsrcs.subtract(participantSsrcs)
self.processedMissingSsrcs.formUnion(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 { if self.missingSsrcs.isEmpty {
return return
} }
if case let .estabilished(callInfo, _, _, _) = self.internalState { if case let .established(callInfo, _, _, _, _) = self.internalState {
self.isRequestingMissingSsrcs = true self.isRequestingMissingSsrcs = true
let requestedSsrcs = self.missingSsrcs let requestedSsrcs = self.missingSsrcs
@ -1267,7 +1344,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
var addedParticipants: [(UInt32, String?)] = [] var addedParticipants: [(UInt32, String?)] = []
for participant in state.participants { for participant in state.participants {
addedParticipants.append((participant.ssrc, participant.jsonParams)) if let ssrc = participant.ssrc {
addedParticipants.append((ssrc, participant.jsonParams))
}
} }
if !addedParticipants.isEmpty { if !addedParticipants.isEmpty {
@ -1283,7 +1362,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if self.checkCallDisposable != nil { if self.checkCallDisposable != nil {
return 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)) let checkSignal = checkGroupCall(account: self.account, callId: callInfo.id, accessHash: callInfo.accessHash, ssrc: Int32(bitPattern: ssrc))
self.checkCallDisposable = (( 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> { 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 { if terminateIfPossible {
self.leaveDisposable.set((stopGroupCall(account: self.account, peerId: self.peerId, callId: callInfo.id, accessHash: callInfo.accessHash) self.leaveDisposable.set((stopGroupCall(account: self.account, peerId: self.peerId, callId: callInfo.id, accessHash: callInfo.accessHash)
|> deliverOnMainQueue).start(completed: { [weak self] in |> 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() { public func requestVideo() {
if self.videoCapturer == nil { if self.videoCapturer == nil {
let videoCapturer = OngoingCallVideoCapturer() let videoCapturer = OngoingCallVideoCapturer()
@ -1576,8 +1681,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
private func requestCall() { private func requestCall() {
self.callContext?.stop() self.callContext?.setConnectionMode(.none)
self.callContext = nil
self.missingSsrcsDisposable.set(nil) self.missingSsrcsDisposable.set(nil)
self.missingSsrcs.removeAll() self.missingSsrcs.removeAll()
@ -1614,7 +1718,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
} }
self.networkStateDisposable.set(nil)
self.joinDisposable.set(nil) self.joinDisposable.set(nil)
self.checkCallDisposable?.dispose()
self.checkCallDisposable = nil
self.stateValue.networkState = .connecting
self.requestDisposable.set((currentOrRequestedCall self.requestDisposable.set((currentOrRequestedCall
|> deliverOnMainQueue).start(next: { [weak self] value in |> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -1632,7 +1743,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
public func invitePeer(_ peerId: PeerId) -> Bool { 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 return false
} }

View File

@ -370,8 +370,8 @@ public final class VoiceChatController: ViewController {
} }
var peer: Peer var peer: Peer
var myPeerId: PeerId var myPeerId: PeerId?
var ssrc: UInt32 var ssrc: UInt32?
var presence: TelegramUserPresence? var presence: TelegramUserPresence?
var activityTimestamp: Int32 var activityTimestamp: Int32
var state: State var state: State
@ -379,6 +379,7 @@ public final class VoiceChatController: ViewController {
var revealed: Bool? var revealed: Bool?
var canManageCall: Bool var canManageCall: Bool
var volume: Int32? var volume: Int32?
var raiseHandStatus: Bool
var stableId: PeerId { var stableId: PeerId {
return self.peer.id return self.peer.id
@ -415,6 +416,9 @@ public final class VoiceChatController: ViewController {
if lhs.volume != rhs.volume { if lhs.volume != rhs.volume {
return false return false
} }
if lhs.raiseHandStatus != rhs.raiseHandStatus {
return false
}
return true return true
} }
@ -508,7 +512,7 @@ public final class VoiceChatController: ViewController {
case let .peer(peerEntry): case let .peer(peerEntry):
let peer = peerEntry.peer let peer = peerEntry.peer
let text: VoiceChatParticipantItem.ParticipantText var text: VoiceChatParticipantItem.ParticipantText
let icon: VoiceChatParticipantItem.Icon let icon: VoiceChatParticipantItem.Icon
switch peerEntry.state { switch peerEntry.state {
case .listening: case .listening:
@ -542,10 +546,18 @@ public final class VoiceChatController: ViewController {
icon = .invite(true) icon = .invite(true)
} }
if peerEntry.raiseHandStatus, case let .text(value, type) = text {
text = .text("[hand] \(value)", type)
}
let revealOptions: [VoiceChatParticipantItem.RevealOption] = [] 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 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 }, revealOptions: revealOptions, revealed: peerEntry.revealed, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId) interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId)
}, action: { node in }, action: { node in
@ -771,7 +783,7 @@ public final class VoiceChatController: ViewController {
switch entry { switch entry {
case let .peer(peer): case let .peer(peer):
if peer.peer.id == peerId { if peer.peer.id == peerId {
let source = peer.ssrc if let source = peer.ssrc {
if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source { if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source {
strongSelf.currentDominantSpeakerWithVideo = (peerId, source) strongSelf.currentDominantSpeakerWithVideo = (peerId, source)
strongSelf.call.setFullSizeVideo(peerId: peerId) strongSelf.call.setFullSizeVideo(peerId: peerId)
@ -782,6 +794,7 @@ public final class VoiceChatController: ViewController {
strongSelf.mainVideoContainer?.updatePeer(peer: nil) strongSelf.mainVideoContainer?.updatePeer(peer: nil)
} }
} }
}
default: default:
break break
} }
@ -827,7 +840,8 @@ public final class VoiceChatController: ViewController {
} }
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
if peer.id == strongSelf.call.myPeerId {
if peer.id == strongSelf.callState?.myPeerId {
return return
} }
if let participant = participant { if let participant = participant {
@ -1050,7 +1064,7 @@ public final class VoiceChatController: ViewController {
f(.default) 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 let callState = strongSelf.callState, (callState.canManageCall || callState.adminIds.contains(strongSelf.context.account.peerId)) {
if callState.adminIds.contains(peer.id) { if callState.adminIds.contains(peer.id) {
if let _ = muteState { if let _ = muteState {
@ -1339,7 +1353,7 @@ public final class VoiceChatController: ViewController {
} }
var levels = levels var levels = levels
if strongSelf.effectiveMuteState != nil { 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)? var maxLevelWithVideo: (PeerId, UInt32, Float)?
@ -1467,6 +1481,16 @@ public final class VoiceChatController: ViewController {
strongSelf.call.setShouldBeRecording(callState.recordingStartTimestamp == nil ? true : false) 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 { if items.isEmpty {
@ -1733,6 +1757,8 @@ public final class VoiceChatController: ViewController {
if case .ended = gestureRecognizer.state { if case .ended = gestureRecognizer.state {
self.hapticFeedback.error() self.hapticFeedback.error()
self.actionButton.layer.addShakeAnimation() self.actionButton.layer.addShakeAnimation()
self.call.raiseHand()
} }
return return
} }
@ -1758,7 +1784,9 @@ public final class VoiceChatController: ViewController {
self.call.setIsMuted(action: .muted(isPushToTalkActive: false)) 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 { if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring)) 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 let memberState: PeerEntry.State
var memberMuteState: GroupCallParticipantsContext.Participant.MuteState? var memberMuteState: GroupCallParticipantsContext.Participant.MuteState?
if member.peer.id == self.call.myPeerId { if member.peer.id == self.callState?.myPeerId {
if muteState == nil { if muteState == nil {
memberState = speakingPeers.contains(member.peer.id) ? .speaking : .listening memberState = speakingPeers.contains(member.peer.id) ? .speaking : .listening
} else { } else {
@ -2522,14 +2550,15 @@ public final class VoiceChatController: ViewController {
entries.append(.peer(PeerEntry( entries.append(.peer(PeerEntry(
peer: member.peer, peer: member.peer,
myPeerId: self.call.myPeerId, myPeerId: self.callState?.myPeerId,
ssrc: member.ssrc, ssrc: member.ssrc,
presence: nil, presence: nil,
activityTimestamp: Int32.max - 1 - index, activityTimestamp: Int32.max - 1 - index,
state: memberState, state: memberState,
muteState: memberMuteState, muteState: memberMuteState,
canManageCall: self.callState?.canManageCall ?? false, canManageCall: self.callState?.canManageCall ?? false,
volume: member.volume volume: member.volume,
raiseHandStatus: member.raiseHandRating != nil
))) )))
index += 1 index += 1
} }
@ -2542,14 +2571,15 @@ public final class VoiceChatController: ViewController {
entries.append(.peer(PeerEntry( entries.append(.peer(PeerEntry(
peer: peer, peer: peer,
myPeerId: self.call.myPeerId, myPeerId: self.callState?.myPeerId,
ssrc: 0, ssrc: nil,
presence: nil, presence: nil,
activityTimestamp: Int32.max - 1 - index, activityTimestamp: Int32.max - 1 - index,
state: .invited, state: .invited,
muteState: nil, muteState: nil,
canManageCall: false, canManageCall: false,
volume: nil volume: nil,
raiseHandStatus: false
))) )))
index += 1 index += 1
} }

View File

@ -1260,7 +1260,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
category = .voiceChat 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): case let .updateChannelUserTyping(_, channelId, topMsgId, userId, type):
if let date = updatesDate, date + 60 > serverTime { if let date = updatesDate, date + 60 > serverTime {
@ -1275,7 +1275,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
category = .thread(threadId) 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): case let .updateEncryptedChatTyping(chatId):
if let date = updatesDate, date + 60 > serverTime { 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 let requestService = self.requestService
return Signal { subscriber in return Signal { subscriber in
let request = MTRequest() let request = MTRequest()
@ -351,9 +351,9 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
request.completed = { (boxedResponse, timestamp, error) -> () in request.completed = { (boxedResponse, timestamp, error) -> () in
if let error = error { if let error = error {
subscriber.putError(error) subscriber.putError((error, timestamp))
} else { } else {
subscriber.putNext((boxedResponse as! BoxedMessage).body) subscriber.putNext(((boxedResponse as! BoxedMessage).body, timestamp))
subscriber.putCompletion() subscriber.putCompletion()
} }
} }

View File

@ -40,7 +40,7 @@ public struct GroupCallSummary: Equatable {
extension GroupCallInfo { extension GroupCallInfo {
init?(_ call: Api.GroupCall) { init?(_ call: Api.GroupCall) {
switch call { 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? var clientParams: String?
if let params = params { if let params = params {
switch params { switch params {
@ -106,8 +106,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
loop: for participant in participants { loop: for participant in participants {
switch participant { 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 let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):
@ -131,7 +130,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
} else if mutedByYou { } else if mutedByYou {
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou) muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou)
} }
var jsonParams: String? let jsonParams: String? = nil
/*if let params = params { /*if let params = params {
switch params { switch params {
case let .dataJSON(data): case let .dataJSON(data):
@ -143,6 +142,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
ssrc: ssrc, ssrc: ssrc,
jsonParams: jsonParams, jsonParams: jsonParams,
joinTimestamp: date, joinTimestamp: date,
raiseHandRating: raiseHandRating,
activityTimestamp: activeDate.flatMap(Double.init), activityTimestamp: activeDate.flatMap(Double.init),
activityRank: nil, activityRank: nil,
muteState: muteState, muteState: muteState,
@ -174,20 +174,16 @@ public func createGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?) -
if let joinAs = joinAs { if let joinAs = joinAs {
return (callPeer, transaction.getPeer(joinAs).flatMap(apiInputPeer)) return (callPeer, transaction.getPeer(joinAs).flatMap(apiInputPeer))
} else { } else {
return (callPeer, nil) return (callPeer, .inputPeerSelf)
} }
} }
|> castError(CreateGroupCallError.self) |> castError(CreateGroupCallError.self)
|> mapToSignal { (inputPeer, inputJoinAs) -> Signal<GroupCallInfo, CreateGroupCallError> in |> mapToSignal { (inputPeer, inputJoinAs) -> Signal<GroupCallInfo, CreateGroupCallError> in
guard let inputPeer = inputPeer else { guard let inputPeer = inputPeer, let _ = inputJoinAs else {
return .fail(.generic) return .fail(.generic)
} }
var flags: Int32 = 0 return account.network.request(Api.functions.phone.createGroupCall(peer: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max)))
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)))
|> mapError { error -> CreateGroupCallError in |> mapError { error -> CreateGroupCallError in
if error.errorDescription == "ANONYMOUS_CALLS_DISABLED" { if error.errorDescription == "ANONYMOUS_CALLS_DISABLED" {
return .anonymousNotAllowed return .anonymousNotAllowed
@ -281,7 +277,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
loop: for participant in participants { loop: for participant in participants {
switch participant { 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 let peerId: PeerId
switch apiPeerId { switch apiPeerId {
@ -305,7 +301,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
} else if mutedByYou { } else if mutedByYou {
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou) muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou)
} }
var jsonParams: String? let jsonParams: String? = nil
/*if let params = params { /*if let params = params {
switch params { switch params {
case let .dataJSON(data): case let .dataJSON(data):
@ -317,6 +313,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
ssrc: ssrc, ssrc: ssrc,
jsonParams: jsonParams, jsonParams: jsonParams,
joinTimestamp: date, joinTimestamp: date,
raiseHandRating: raiseHandRating,
activityTimestamp: activeDate.flatMap(Double.init), activityTimestamp: activeDate.flatMap(Double.init),
activityRank: nil, activityRank: nil,
muteState: muteState, muteState: muteState,
@ -352,12 +349,17 @@ public enum JoinGroupCallError {
} }
public struct JoinGroupCallResult { public struct JoinGroupCallResult {
public enum ConnectionMode {
case rtc
case broadcast
}
public var callInfo: GroupCallInfo public var callInfo: GroupCallInfo
public var state: GroupCallParticipantsContext.State 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> { 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 return account.postbox.transaction { transaction -> Api.InputPeer? in
if let joinAs = joinAs { if let joinAs = joinAs {
return transaction.getPeer(joinAs).flatMap(apiInputPeer) return transaction.getPeer(joinAs).flatMap(apiInputPeer)
@ -367,16 +369,15 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
} }
|> castError(JoinGroupCallError.self) |> castError(JoinGroupCallError.self)
|> mapToSignal { inputJoinAs in |> mapToSignal { inputJoinAs in
guard let inputJoinAs = inputJoinAs else {
return .fail(.generic)
}
var flags: Int32 = 0 var flags: Int32 = 0
if preferMuted { if preferMuted {
flags |= (1 << 0) 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))) 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 |> mapError { error -> JoinGroupCallError in
if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" { if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" {
@ -491,7 +492,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
maybeParsedCall = GroupCallInfo(call) maybeParsedCall = GroupCallInfo(call)
switch call { switch call {
case let .groupCall(flags, _, _, _, _, title, streamDcId, recordStartDate, _): case let .groupCall(flags, _, _, _, _, title, _, recordStartDate, _):
let isMuted = (flags & (1 << 1)) != 0 let isMuted = (flags & (1 << 1)) != 0
let canChange = (flags & (1 << 2)) != 0 let canChange = (flags & (1 << 2)) != 0
state.defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange) 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 state.adminIds = adminIds
switch result { switch result {
case let .groupCall(call, _, _, chats, users): case let .groupCall(call, participants, _, chats, users):
guard let _ = GroupCallInfo(call) else { guard let _ = GroupCallInfo(call) else {
return .fail(.generic) 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 return account.postbox.transaction { transaction -> JoinGroupCallResult in
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated return updated
@ -551,7 +563,8 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
return JoinGroupCallResult( return JoinGroupCallResult(
callInfo: parsedCall, callInfo: parsedCall,
state: state state: state,
connectionMode: connectionMode
) )
} }
|> castError(JoinGroupCallError.self) |> castError(JoinGroupCallError.self)
@ -635,6 +648,9 @@ public func checkGroupCall(account: Account, callId: Int64, accessHash: Int64, s
return .single(.boolFalse) return .single(.boolFalse)
} }
|> map { result -> CheckGroupCallResult in |> map { result -> CheckGroupCallResult in
#if DEBUG
//return .restart
#endif
switch result { switch result {
case .boolTrue: case .boolTrue:
return .success return .success
@ -673,9 +689,10 @@ public final class GroupCallParticipantsContext {
} }
public var peer: Peer public var peer: Peer
public var ssrc: UInt32 public var ssrc: UInt32?
public var jsonParams: String? public var jsonParams: String?
public var joinTimestamp: Int32 public var joinTimestamp: Int32
public var raiseHandRating: Int64?
public var activityTimestamp: Double? public var activityTimestamp: Double?
public var activityRank: Int? public var activityRank: Int?
public var muteState: MuteState? public var muteState: MuteState?
@ -683,9 +700,10 @@ public final class GroupCallParticipantsContext {
public var about: String? public var about: String?
public init( public init(
peer: Peer, peer: Peer,
ssrc: UInt32, ssrc: UInt32?,
jsonParams: String?, jsonParams: String?,
joinTimestamp: Int32, joinTimestamp: Int32,
raiseHandRating: Int64?,
activityTimestamp: Double?, activityTimestamp: Double?,
activityRank: Int?, activityRank: Int?,
muteState: MuteState?, muteState: MuteState?,
@ -696,6 +714,7 @@ public final class GroupCallParticipantsContext {
self.ssrc = ssrc self.ssrc = ssrc
self.jsonParams = jsonParams self.jsonParams = jsonParams
self.joinTimestamp = joinTimestamp self.joinTimestamp = joinTimestamp
self.raiseHandRating = raiseHandRating
self.activityTimestamp = activityTimestamp self.activityTimestamp = activityTimestamp
self.activityRank = activityRank self.activityRank = activityRank
self.muteState = muteState self.muteState = muteState
@ -713,6 +732,9 @@ public final class GroupCallParticipantsContext {
if lhs.joinTimestamp != rhs.joinTimestamp { if lhs.joinTimestamp != rhs.joinTimestamp {
return false return false
} }
if lhs.raiseHandRating != rhs.raiseHandRating {
return false
}
if lhs.activityTimestamp != rhs.activityTimestamp { if lhs.activityTimestamp != rhs.activityTimestamp {
return false return false
} }
@ -752,6 +774,16 @@ public final class GroupCallParticipantsContext {
return false 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 { if lhs.joinTimestamp != rhs.joinTimestamp {
return lhs.joinTimestamp > rhs.joinTimestamp return lhs.joinTimestamp > rhs.joinTimestamp
} }
@ -822,20 +854,22 @@ public final class GroupCallParticipantsContext {
} }
public var peerId: PeerId public var peerId: PeerId
public var ssrc: UInt32 public var ssrc: UInt32?
public var jsonParams: String? public var jsonParams: String?
public var joinTimestamp: Int32 public var joinTimestamp: Int32
public var activityTimestamp: Double? public var activityTimestamp: Double?
public var raiseHandRating: Int64?
public var muteState: Participant.MuteState? public var muteState: Participant.MuteState?
public var participationStatusChange: ParticipationStatusChange public var participationStatusChange: ParticipationStatusChange
public var volume: Int32? public var volume: Int32?
public var about: String? public var about: String?
init( init(
peerId: PeerId, peerId: PeerId,
ssrc: UInt32, ssrc: UInt32?,
jsonParams: String?, jsonParams: String?,
joinTimestamp: Int32, joinTimestamp: Int32,
activityTimestamp: Double?, activityTimestamp: Double?,
raiseHandRating: Int64?,
muteState: Participant.MuteState?, muteState: Participant.MuteState?,
participationStatusChange: ParticipationStatusChange, participationStatusChange: ParticipationStatusChange,
volume: Int32?, volume: Int32?,
@ -846,6 +880,7 @@ public final class GroupCallParticipantsContext {
self.jsonParams = jsonParams self.jsonParams = jsonParams
self.joinTimestamp = joinTimestamp self.joinTimestamp = joinTimestamp
self.activityTimestamp = activityTimestamp self.activityTimestamp = activityTimestamp
self.raiseHandRating = raiseHandRating
self.muteState = muteState self.muteState = muteState
self.participationStatusChange = participationStatusChange self.participationStatusChange = participationStatusChange
self.volume = volume self.volume = volume
@ -874,6 +909,7 @@ public final class GroupCallParticipantsContext {
} }
private let account: Account private let account: Account
private let myPeerId: PeerId
private let id: Int64 private let id: Int64
private let accessHash: Int64 private let accessHash: Int64
@ -939,8 +975,9 @@ public final class GroupCallParticipantsContext {
private let updateDefaultMuteDisposable = MetaDisposable() private let updateDefaultMuteDisposable = MetaDisposable()
private let updateShouldBeRecordingDisposable = 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.account = account
self.myPeerId = myPeerId
self.id = id self.id = id
self.accessHash = accessHash self.accessHash = accessHash
self.stateValue = InternalState(state: state, overlayState: OverlayState()) self.stateValue = InternalState(state: state, overlayState: OverlayState())
@ -1002,14 +1039,6 @@ public final class GroupCallParticipantsContext {
if updated { if updated {
updatedParticipants.sort() 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( strongSelf.stateValue = InternalState(
state: State( state: State(
@ -1134,14 +1163,6 @@ public final class GroupCallParticipantsContext {
if updated { if updated {
updatedParticipants.sort() 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( strongSelf.stateValue = InternalState(
state: State( state: State(
@ -1167,7 +1188,9 @@ public final class GroupCallParticipantsContext {
var existingSsrcs = Set<UInt32>() var existingSsrcs = Set<UInt32>()
for participant in self.stateValue.state.participants { for participant in self.stateValue.state.participants {
existingSsrcs.insert(participant.ssrc) if let ssrc = participant.ssrc {
existingSsrcs.insert(ssrc)
}
} }
for ssrc in ssrcs { for ssrc in ssrcs {
@ -1312,6 +1335,7 @@ public final class GroupCallParticipantsContext {
ssrc: participantUpdate.ssrc, ssrc: participantUpdate.ssrc,
jsonParams: participantUpdate.jsonParams, jsonParams: participantUpdate.jsonParams,
joinTimestamp: participantUpdate.joinTimestamp, joinTimestamp: participantUpdate.joinTimestamp,
raiseHandRating: participantUpdate.raiseHandRating,
activityTimestamp: activityTimestamp, activityTimestamp: activityTimestamp,
activityRank: previousActivityRank, activityRank: previousActivityRank,
muteState: participantUpdate.muteState, muteState: participantUpdate.muteState,
@ -1406,6 +1430,7 @@ public final class GroupCallParticipantsContext {
let account = self.account let account = self.account
let id = self.id let id = self.id
let accessHash = self.accessHash let accessHash = self.accessHash
let myPeerId = self.myPeerId
let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer) return transaction.getPeer(peerId).flatMap(apiInputPeer)
@ -1418,11 +1443,11 @@ public final class GroupCallParticipantsContext {
if let volume = volume, volume > 0 { if let volume = volume, volume > 0 {
flags |= 1 << 1 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 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) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in |> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil) 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) { public func updateShouldBeRecording(_ shouldBeRecording: Bool) {
var flags: Int32 = 0 var flags: Int32 = 0
if shouldBeRecording { if shouldBeRecording {
@ -1528,7 +1565,7 @@ public final class GroupCallParticipantsContext {
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
init(_ apiParticipant: Api.GroupCallParticipant) { init(_ apiParticipant: Api.GroupCallParticipant) {
switch apiParticipant { 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 let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):
@ -1560,7 +1597,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
participationStatusChange = .none participationStatusChange = .none
} }
var jsonParams: String? let jsonParams: String? = nil
/*if let params = params { /*if let params = params {
switch params { switch params {
case let .dataJSON(data): case let .dataJSON(data):
@ -1574,6 +1611,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
jsonParams: jsonParams, jsonParams: jsonParams,
joinTimestamp: date, joinTimestamp: date,
activityTimestamp: activeDate.flatMap(Double.init), activityTimestamp: activeDate.flatMap(Double.init),
raiseHandRating: raiseHandRating,
muteState: muteState, muteState: muteState,
participationStatusChange: participationStatusChange, participationStatusChange: participationStatusChange,
volume: volume, volume: volume,
@ -1588,7 +1626,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = [] var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = []
for participant in participants { for participant in participants {
switch participant { 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 let peerId: PeerId
switch apiPeerId { switch apiPeerId {
case let .peerUser(userId): case let .peerUser(userId):
@ -1620,7 +1658,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
participationStatusChange = .none participationStatusChange = .none
} }
var jsonParams: String? let jsonParams: String? = nil
/*if let params = params { /*if let params = params {
switch params { switch params {
case let .dataJSON(data): case let .dataJSON(data):
@ -1634,6 +1672,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
jsonParams: jsonParams, jsonParams: jsonParams,
joinTimestamp: date, joinTimestamp: date,
activityTimestamp: activeDate.flatMap(Double.init), activityTimestamp: activeDate.flatMap(Double.init),
raiseHandRating: raiseHandRating,
muteState: muteState, muteState: muteState,
participationStatusChange: participationStatusChange, participationStatusChange: participationStatusChange,
volume: volume, 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> { public func groupCallDisplayAsAvailablePeers(network: Network, postbox: Postbox) -> Signal<[FoundPeer], NoError> {
return network.request(Api.functions.channels.getAdminedPublicChannels(flags: 1 << 2)) return network.request(Api.functions.channels.getAdminedPublicChannels(flags: 1 << 2))
|> retryRequest |> retryRequest
@ -1713,20 +1749,18 @@ public func groupCallDisplayAsAvailablePeers(network: Network, postbox: Postbox)
for chat in chats { for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
switch chat { switch chat {
case let .channel(channel): case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount):
if let participantsCount = channel.participantsCount { if let participantsCount = participantsCount {
subscribers[groupOrChannel.id] = participantsCount subscribers[groupOrChannel.id] = participantsCount
} }
case let .chat(chat): case let .chat(_, _, _, _, participantsCount, _, _, _, _, _):
subscribers[groupOrChannel.id] = chat.participantsCount subscribers[groupOrChannel.id] = participantsCount
default: default:
break break
} }
} }
} }
return postbox.transaction { transaction -> [Peer] in return postbox.transaction { transaction -> [Peer] in
updatePeers(transaction: transaction, peers: peers, update: { _, updated in updatePeers(transaction: transaction, peers: peers, update: { _, updated in
return updated return updated
@ -1768,21 +1802,67 @@ private func mergeAndSortParticipants(current currentParticipants: [GroupCallPar
return mergedParticipants return mergedParticipants
} }
public func getAudioBroadcastPart(account: Account, callId: Int64, accessHash: Int64, datacenterId: Int?, timestampId: Int32) -> Signal<Data?, NoError> { public func getAudioBroadcastDatacenter(account: Account, callId: Int64, accessHash: Int64) -> Signal<Int?, 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) return account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.upload.File?, NoError> in |> `catch` { _ -> Signal<Api.phone.GroupCall?, NoError> in
return .single(nil) return .single(nil)
} }
|> map { result -> Data? in |> map { result -> Int? in
guard let result = result else { guard let result = result else {
return nil return nil
} }
switch result { switch result {
case let .file(_, _, bytes): case let .groupCall(call, _, _, _, _):
return bytes.makeData() return GroupCallInfo(call)?.streamDcId.flatMap(Int.init)
case .fileCdnRedirect: }
return nil }
}
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 self.bigParts = true
} else if useLargerParts { } else if useLargerParts {
self.bigParts = false self.bigParts = false
self.defaultPartSize = 128 * 1024 self.defaultPartSize = 256 * 1024
self.bigTotalParts = nil self.bigTotalParts = nil
} else { } else {
self.bigParts = false self.bigParts = false
@ -218,7 +218,7 @@ private final class MultipartUploadManager {
} else { } else {
self.bigParts = false self.bigParts = false
if self.useLargerParts { if self.useLargerParts {
self.defaultPartSize = 128 * 1024 self.defaultPartSize = 256 * 1024
} else { } else {
self.defaultPartSize = 16 * 1024 self.defaultPartSize = 16 * 1024
} }

View File

@ -24,10 +24,10 @@ private final class RequestData {
let continueInBackground: Bool let continueInBackground: Bool
let automaticFloodWait: Bool let automaticFloodWait: Bool
let deserializeResponse: (Buffer) -> Any? let deserializeResponse: (Buffer) -> Any?
let completed: (Any) -> Void let completed: (Any, Double) -> Void
let error: (MTRpcError) -> 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.id = id
self.consumerId = consumerId self.consumerId = consumerId
self.target = target 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 targetKey = MultiplexedRequestTargetKey(target: target, continueInBackground: continueInBackground)
let requestId = self.nextId let requestId = self.nextId
self.nextId += 1 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 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) return data.2(buffer)
}, completed: { result in }, completed: { result, timestamp in
completed(result) completed(result, timestamp)
}, error: { e in }, error: { e, timestamp in
error(e) error(e, timestamp)
})) }))
self.updateState() self.updateState()
@ -180,7 +180,7 @@ private final class MultiplexedRequestManagerContext {
let requestId = request.id let requestId = request.id
selectedContext.requests.append(ExecutingRequestData(requestId: requestId, disposable: disposable)) selectedContext.requests.append(ExecutingRequestData(requestId: requestId, disposable: disposable))
let queue = self.queue 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 { queue.async {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -193,15 +193,15 @@ private final class MultiplexedRequestManagerContext {
} }
} }
} }
request.completed(result) request.completed(result, timestamp)
strongSelf.updateState() strongSelf.updateState()
} }
}, error: { [weak self, weak selectedContext] error in }, error: { [weak self, weak selectedContext] error, timestamp in
queue.async { queue.async {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
request.error(error) request.error(error, timestamp)
if let selectedContext = selectedContext { if let selectedContext = selectedContext {
for i in 0 ..< selectedContext.requests.count { for i in 0 ..< selectedContext.requests.count {
if selectedContext.requests[i].requestId == requestId { if selectedContext.requests[i].requestId == requestId {
@ -275,18 +275,39 @@ final class MultiplexedRequestManager {
self.context.with { context in self.context.with { context in
disposable.set(context.request(to: target, consumerId: consumerId, data: (data.0, data.1, { buffer in disposable.set(context.request(to: target, consumerId: consumerId, data: (data.0, data.1, { buffer in
return data.2.parse(buffer) 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 { if let result = result as? T {
subscriber.putNext(result) subscriber.putNext(result)
subscriber.putCompletion() subscriber.putCompletion()
} else { } else {
subscriber.putError(MTRpcError(errorCode: 500, errorDescription: "TL_VERIFICATION_ERROR")) subscriber.putError(MTRpcError(errorCode: 500, errorDescription: "TL_VERIFICATION_ERROR"))
} }
}, error: { error in }, error: { error, _ in
subscriber.putError(error) subscriber.putError(error)
})) }))
} }
return disposable 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 { 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 { private final class NetworkBroadcastPartSource: BroadcastPartSource {
@ -40,17 +40,9 @@ private final class NetworkBroadcastPartSource: BroadcastPartSource {
private let account: Account private let account: Account
private let callId: Int64 private let callId: Int64
private let accessHash: 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?) { init(queue: Queue, account: Account, callId: Int64, accessHash: Int64, datacenterId: Int?) {
self.queue = queue self.queue = queue
@ -58,44 +50,69 @@ private final class NetworkBroadcastPartSource: BroadcastPartSource {
self.callId = callId self.callId = callId
self.accessHash = accessHash self.accessHash = accessHash
self.datacenterId = datacenterId self.datacenterId = datacenterId
self.check()
} }
deinit { func requestPart(timestamp: Int32, completion: @escaping (OngoingGroupCallBroadcastPart) -> Void, rejoinNeeded: @escaping () -> Void) -> Disposable {
self.timer?.invalidate() let timestampId = timestamp == 0 ? Int32(Date().timeIntervalSince1970) : timestamp
self.disposable.dispose()
}
private func check() { let datacenterId: Signal<Int?, NoError>
let timestampId: Int32 if let datacenterIdValue = self.datacenterId {
if let nextTimestampId = self.nextTimestampId { datacenterId = .single(datacenterIdValue)
timestampId = nextTimestampId
} else { } 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 let account = self.account
guard let strongSelf = self else { 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 return
} }
if let data = data { let part: OngoingGroupCallBroadcastPart
var parts: [OngoingGroupCallBroadcastPart] = [] switch result.status {
parts.append(OngoingGroupCallBroadcastPart(timestamp: timestampId, oggData: data)) case let .data(dataValue):
part = OngoingGroupCallBroadcastPart(timestamp: timestampId, responseTimestamp: result.responseTimestamp, status: .success, oggData: dataValue)
strongSelf.nextTimestampId = timestampId + 1 case .notReady:
part = OngoingGroupCallBroadcastPart(timestamp: timestampId, responseTimestamp: result.responseTimestamp, status: .notReady, oggData: Data())
if !parts.isEmpty { case .tooOld:
strongSelf.partsPipe.putNext(parts) part = OngoingGroupCallBroadcastPart(timestamp: timestampId, responseTimestamp: result.responseTimestamp, status: .tooOld, oggData: Data())
} case .rejoinNeeded:
rejoinNeeded()
return
} }
strongSelf.timer?.invalidate() completion(part)
strongSelf.timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: false, completion: { })
self?.check() }
}, queue: strongSelf.queue) }
strongSelf.timer?.start()
})) 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 { public enum NetworkState {
case connecting case connecting
case connected case connected
@ -138,14 +161,20 @@ public final class OngoingGroupCallContext {
let videoSources = ValuePromise<Set<UInt32>>(Set(), ignoreRepeated: true) let videoSources = ValuePromise<Set<UInt32>>(Set(), ignoreRepeated: true)
private var broadcastPartsSource: BroadcastPartSource? 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 self.queue = queue
var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)? var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)?
var audioLevelsUpdatedImpl: (([NSNumber]) -> 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 let videoSources = self.videoSources
self.context = GroupCallThreadLocalContext( self.context = GroupCallThreadLocalContext(
queue: ContextQueueImpl(queue: queue), queue: ContextQueueImpl(queue: queue),
@ -164,34 +193,17 @@ public final class OngoingGroupCallContext {
participantDescriptionsRequired: { ssrcs in participantDescriptionsRequired: { ssrcs in
participantDescriptionsRequired(Set(ssrcs.map { $0.uint32Value })) participantDescriptionsRequired(Set(ssrcs.map { $0.uint32Value }))
}, },
externalDecodeOgg: { sourceData in requestBroadcastPart: { timestamp, completion in
let tempFile = TempBox.shared.tempFile(fileName: "audio.ogg") let disposable = MetaDisposable()
defer {
TempBox.shared.dispose(tempFile) 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 OngoingGroupCallBroadcastPartTaskImpl(disposable: disposable)
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
) )
let queue = self.queue let queue = self.queue
@ -242,23 +254,9 @@ public final class OngoingGroupCallContext {
strongSelf.joinPayload.set(.single((payload, ssrc))) 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 { deinit {
self.broadcastPartsDisposable?.dispose()
} }
func setJoinResponse(payload: String, participants: [(UInt32, String?)]) { func setJoinResponse(payload: String, participants: [(UInt32, String?)]) {
@ -300,6 +298,33 @@ public final class OngoingGroupCallContext {
self.context.stop() 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) { func setIsMuted(_ isMuted: Bool) {
self.isMuted.set(isMuted) self.isMuted.set(isMuted)
self.context.setIsMuted(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 let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: { 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) { public func setIsMuted(_ isMuted: Bool) {
self.impl.with { impl in self.impl.with { impl in
impl.setIsMuted(isMuted) impl.setIsMuted(isMuted)

View File

@ -20,6 +20,7 @@ objc_library(
"tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*", "tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*",
"tgcalls/tgcalls/platform/darwin/GLVideoViewMac.*", "tgcalls/tgcalls/platform/darwin/GLVideoViewMac.*",
"tgcalls/tgcalls/platform/darwin/ScreenCapturer.*", "tgcalls/tgcalls/platform/darwin/ScreenCapturer.*",
"tgcalls/tgcalls/desktop_capturer/**",
]), ]),
hdrs = glob([ hdrs = glob([
"PublicHeaders/**/*.h", "PublicHeaders/**/*.h",

View File

@ -165,21 +165,43 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) {
@end @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 @interface OngoingGroupCallBroadcastPart : NSObject
@property (nonatomic, readonly) int32_t timestamp; @property (nonatomic, readonly) int32_t timestamp;
@property (nonatomic, readonly) double responseTimestamp;
@property (nonatomic, readonly) OngoingGroupCallBroadcastPartStatus status;
@property (nonatomic, strong, readonly) NSData * _Nonnull oggData; @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 @end
@interface GroupCallThreadLocalContext : NSObject @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)stop;
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode;
- (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion; - (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
- (void)setJoinResponsePayload:(NSString * _Nonnull)payload participants:(NSArray<OngoingGroupCallParticipantDescription *> * _Nonnull)participants; - (void)setJoinResponsePayload:(NSString * _Nonnull)payload participants:(NSArray<OngoingGroupCallParticipantDescription *> * _Nonnull)participants;
- (void)removeSsrcs:(NSArray<NSNumber *> * _Nonnull)ssrcs; - (void)removeSsrcs:(NSArray<NSNumber *> * _Nonnull)ssrcs;
@ -195,8 +217,6 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) {
- (void)switchAudioInput:(NSString * _Nonnull)deviceId; - (void)switchAudioInput:(NSString * _Nonnull)deviceId;
- (void)makeIncomingVideoViewWithSsrc:(uint32_t)ssrc completion:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion; - (void)makeIncomingVideoViewWithSsrc:(uint32_t)ssrc completion:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
- (void)addBroadcastParts:(NSArray<OngoingGroupCallBroadcastPart *> * _Nonnull)parts;
@end @end
#endif #endif

View File

@ -819,6 +819,26 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
@end @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 () { @interface GroupCallThreadLocalContext () {
id<OngoingCallThreadLocalContextQueueWebrtc> _queue; id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
@ -833,7 +853,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
@implementation GroupCallThreadLocalContext @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]; self = [super init];
if (self != nil) { if (self != nil) {
_queue = queue; _queue = queue;
@ -841,19 +861,6 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
_networkStateUpdated = [networkStateUpdated copy]; _networkStateUpdated = [networkStateUpdated copy];
_videoCapturer = videoCapturer; _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; __weak GroupCallThreadLocalContext *weakSelf = self;
_instance.reset(new tgcalls::GroupInstanceCustomImpl((tgcalls::GroupInstanceDescriptor){ _instance.reset(new tgcalls::GroupInstanceCustomImpl((tgcalls::GroupInstanceDescriptor){
.networkStateUpdated = [weakSelf, queue, networkStateUpdated](bool isConnected) { .networkStateUpdated = [weakSelf, queue, networkStateUpdated](bool isConnected) {
@ -891,8 +898,41 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
} }
participantDescriptionsRequired(mappedSources); participantDescriptionsRequired(mappedSources);
}, },
.externalDecodeOgg = externalDecodeOggFunction, .requestBroadcastPart = [requestBroadcastPart](int32_t timestamp, std::function<void(tgcalls::BroadcastPart &&)> completion) -> std::shared_ptr<tgcalls::BroadcastPartTask> {
.disableIncomingChannels = disableIncomingChannels 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; return self;
@ -1001,6 +1041,31 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn
completion(string, payload.ssrc); 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 { - (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion {
if (_instance) { if (_instance) {
_instance->emitJoinPayload([completion](tgcalls::GroupJoinPayload payload) { _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 @end
@implementation OngoingGroupCallParticipantDescription @implementation OngoingGroupCallParticipantDescription
@ -1447,10 +1495,12 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn
@implementation OngoingGroupCallBroadcastPart @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]; self = [super init];
if (self != nil) { if (self != nil) {
_timestamp = timestamp; _timestamp = timestamp;
_responseTimestamp = responseTimestamp;
_status = status;
_oggData = oggData; _oggData = oggData;
} }
return self; return self;

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