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