mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Conference calls
This commit is contained in:
parent
166df8406f
commit
7e9b1fcc40
@ -164,7 +164,8 @@ public protocol PresentationCall: AnyObject {
|
||||
func setCurrentAudioOutput(_ output: AudioSessionOutput)
|
||||
func debugInfo() -> Signal<(String, String), NoError>
|
||||
|
||||
func makeIncomingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
func createConferenceIfPossible()
|
||||
|
||||
func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
}
|
||||
|
||||
@ -395,7 +396,7 @@ public protocol PresentationGroupCall: AnyObject {
|
||||
var account: Account { get }
|
||||
var accountContext: AccountContext { get }
|
||||
var internalId: CallSessionInternalId { get }
|
||||
var peerId: EnginePeer.Id { get }
|
||||
var peerId: EnginePeer.Id? { get }
|
||||
|
||||
var hasVideo: Bool { get }
|
||||
var hasScreencast: Bool { get }
|
||||
@ -459,7 +460,6 @@ public protocol PresentationGroupCall: AnyObject {
|
||||
|
||||
var inviteLinks: Signal<GroupCallInviteLinks?, NoError> { get }
|
||||
|
||||
func makeIncomingVideoView(endpointId: String, requestClone: Bool, completion: @escaping (PresentationCallVideoView?, PresentationCallVideoView?) -> Void)
|
||||
func makeOutgoingVideoView(requestClone: Bool, completion: @escaping (PresentationCallVideoView?, PresentationCallVideoView?) -> Void)
|
||||
|
||||
func loadMoreMembers(token: String)
|
||||
|
@ -3609,7 +3609,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
if case let .Video(_, _, _, _, _, videoCodec) = attribute, let videoCodec {
|
||||
qualityDebugText += " \(videoCodec)"
|
||||
if videoCodec == "av1" || videoCodec == "av01" {
|
||||
qualityDebugText += isHardwareAv1Supported ? " (HW)" : " (SW)"
|
||||
qualityDebugText += internal_isHardwareAv1Supported ? " (HW)" : " (SW)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import SwiftSignalKit
|
||||
import Postbox
|
||||
import VideoToolbox
|
||||
|
||||
public let isHardwareAv1Supported: Bool = {
|
||||
public let internal_isHardwareAv1Supported: Bool = {
|
||||
let value = VTIsHardwareDecodeSupported(kCMVideoCodecType_AV1)
|
||||
return value
|
||||
}()
|
||||
@ -39,7 +39,7 @@ public final class ChunkMediaPlayerV2: ChunkMediaPlayer {
|
||||
|
||||
func load() {
|
||||
let reader: MediaDataReader
|
||||
if self.mediaType == .video && (self.codecName == "av1" || self.codecName == "av01") && isHardwareAv1Supported {
|
||||
if self.mediaType == .video && (self.codecName == "av1" || self.codecName == "av01") && internal_isHardwareAv1Supported {
|
||||
reader = AVAssetVideoDataReader(filePath: self.tempFile.path, isVideo: self.mediaType == .video)
|
||||
} else {
|
||||
reader = FFMpegMediaDataReader(filePath: self.tempFile.path, isVideo: self.mediaType == .video, codecName: self.codecName)
|
||||
|
@ -36,7 +36,7 @@ public final class FFMpegMediaDataReader: MediaDataReader {
|
||||
|
||||
if self.isVideo {
|
||||
var passthroughDecoder = true
|
||||
if (codecName == "av1" || codecName == "av01") && !isHardwareAv1Supported {
|
||||
if (codecName == "av1" || codecName == "av01") && !internal_isHardwareAv1Supported {
|
||||
passthroughDecoder = false
|
||||
}
|
||||
let videoSource = SoftwareVideoReader(path: filePath, hintVP9: false, passthroughDecoder: passthroughDecoder)
|
||||
|
@ -296,7 +296,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
|
||||
dict[-565420653] = { return Api.GeoPointAddress.parse_geoPointAddress($0) }
|
||||
dict[1934380235] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) }
|
||||
dict[-711498484] = { return Api.GroupCall.parse_groupCall($0) }
|
||||
dict[-839330845] = { return Api.GroupCall.parse_groupCall($0) }
|
||||
dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) }
|
||||
dict[-341428482] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||
dict[1735736008] = { return Api.GroupCallParticipantVideo.parse_groupCallParticipantVideo($0) }
|
||||
@ -722,7 +722,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1721619444] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) }
|
||||
dict[-1395233698] = { return Api.PeerSettings.parse_peerSettings($0) }
|
||||
dict[-1707742823] = { return Api.PeerStories.parse_peerStories($0) }
|
||||
dict[810769141] = { return Api.PhoneCall.parse_phoneCall($0) }
|
||||
dict[1000707084] = { return Api.PhoneCall.parse_phoneCall($0) }
|
||||
dict[912311057] = { return Api.PhoneCall.parse_phoneCallAccepted($0) }
|
||||
dict[1355435489] = { return Api.PhoneCall.parse_phoneCallDiscarded($0) }
|
||||
dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) }
|
||||
@ -1030,7 +1030,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-451831443] = { return Api.Update.parse_updateFavedStickers($0) }
|
||||
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
|
||||
dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) }
|
||||
dict[347227392] = { return Api.Update.parse_updateGroupCall($0) }
|
||||
dict[-1747565759] = { return Api.Update.parse_updateGroupCall($0) }
|
||||
dict[192428418] = { return Api.Update.parse_updateGroupCallConnection($0) }
|
||||
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
|
||||
dict[1763610706] = { return Api.Update.parse_updateInlineBotCallbackQuery($0) }
|
||||
|
@ -1010,7 +1010,7 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum PhoneCall: TypeConstructorDescription {
|
||||
case phoneCall(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connections: [Api.PhoneConnection], startDate: Int32, customParameters: Api.DataJSON?)
|
||||
case phoneCall(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connections: [Api.PhoneConnection], startDate: Int32, customParameters: Api.DataJSON?, conferenceCall: Api.InputGroupCall?)
|
||||
case phoneCallAccepted(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gB: Buffer, protocol: Api.PhoneCallProtocol)
|
||||
case phoneCallDiscarded(flags: Int32, id: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?)
|
||||
case phoneCallEmpty(id: Int64)
|
||||
@ -1019,9 +1019,9 @@ public extension Api {
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters):
|
||||
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters, let conferenceCall):
|
||||
if boxed {
|
||||
buffer.appendInt32(810769141)
|
||||
buffer.appendInt32(1000707084)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
@ -1039,6 +1039,7 @@ public extension Api {
|
||||
}
|
||||
serializeInt32(startDate, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 7) != 0 {customParameters!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
|
||||
break
|
||||
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`):
|
||||
if boxed {
|
||||
@ -1099,8 +1100,8 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters):
|
||||
return ("phoneCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any), ("`protocol`", `protocol` as Any), ("connections", connections as Any), ("startDate", startDate as Any), ("customParameters", customParameters as Any)])
|
||||
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters, let conferenceCall):
|
||||
return ("phoneCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any), ("`protocol`", `protocol` as Any), ("connections", connections as Any), ("startDate", startDate as Any), ("customParameters", customParameters as Any), ("conferenceCall", conferenceCall as Any)])
|
||||
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`):
|
||||
return ("phoneCallAccepted", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gB", gB as Any), ("`protocol`", `protocol` as Any)])
|
||||
case .phoneCallDiscarded(let flags, let id, let reason, let duration):
|
||||
@ -1145,6 +1146,10 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 7) != 0 {if let signature = reader.readInt32() {
|
||||
_12 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
} }
|
||||
var _13: Api.InputGroupCall?
|
||||
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
|
||||
_13 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -1157,8 +1162,9 @@ public extension Api {
|
||||
let _c10 = _10 != nil
|
||||
let _c11 = _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.PhoneCall.phoneCall(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAOrB: _7!, keyFingerprint: _8!, protocol: _9!, connections: _10!, startDate: _11!, customParameters: _12)
|
||||
let _c13 = (Int(_1!) & Int(1 << 8) == 0) || _13 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
|
||||
return Api.PhoneCall.phoneCall(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAOrB: _7!, keyFingerprint: _8!, protocol: _9!, connections: _10!, startDate: _11!, customParameters: _12, conferenceCall: _13)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -789,7 +789,7 @@ public extension Api {
|
||||
case updateFavedStickers
|
||||
case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32)
|
||||
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
|
||||
case updateGroupCall(chatId: Int64, call: Api.GroupCall)
|
||||
case updateGroupCall(flags: Int32, chatId: Int64?, call: Api.GroupCall)
|
||||
case updateGroupCallConnection(flags: Int32, params: Api.DataJSON)
|
||||
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
|
||||
case updateInlineBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, msgId: Api.InputBotInlineMessageID, chatInstance: Int64, data: Buffer?, gameShortName: String?)
|
||||
@ -1471,11 +1471,12 @@ public extension Api {
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateGroupCall(let chatId, let call):
|
||||
case .updateGroupCall(let flags, let chatId, let call):
|
||||
if boxed {
|
||||
buffer.appendInt32(347227392)
|
||||
buffer.appendInt32(-1747565759)
|
||||
}
|
||||
serializeInt64(chatId, buffer: buffer, boxed: false)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(chatId!, buffer: buffer, boxed: false)}
|
||||
call.serialize(buffer, true)
|
||||
break
|
||||
case .updateGroupCallConnection(let flags, let params):
|
||||
@ -2231,8 +2232,8 @@ public extension Api {
|
||||
return ("updateFolderPeers", [("folderPeers", folderPeers as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
||||
case .updateGeoLiveViewed(let peer, let msgId):
|
||||
return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)])
|
||||
case .updateGroupCall(let chatId, let call):
|
||||
return ("updateGroupCall", [("chatId", chatId as Any), ("call", call as Any)])
|
||||
case .updateGroupCall(let flags, let chatId, let call):
|
||||
return ("updateGroupCall", [("flags", flags as Any), ("chatId", chatId as Any), ("call", call as Any)])
|
||||
case .updateGroupCallConnection(let flags, let params):
|
||||
return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)])
|
||||
case .updateGroupCallParticipants(let call, let participants, let version):
|
||||
@ -3653,16 +3654,19 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public static func parse_updateGroupCall(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Api.GroupCall?
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt64() }
|
||||
var _3: Api.GroupCall?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.GroupCall
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.GroupCall
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.Update.updateGroupCall(chatId: _1!, call: _2!)
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateGroupCall(flags: _1!, chatId: _2, call: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -9586,6 +9586,21 @@ public extension Api.functions.phone {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.phone {
|
||||
static func createConferenceCall(peer: Api.InputPhoneCall) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1828162221)
|
||||
peer.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "phone.createConferenceCall", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.phone.PhoneCall?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.phone {
|
||||
static func createGroupCall(flags: Int32, peer: Api.InputPeer, randomId: Int32, title: String?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -982,14 +982,14 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum GroupCall: TypeConstructorDescription {
|
||||
case groupCall(flags: Int32, id: Int64, accessHash: Int64, participantsCount: Int32, title: String?, streamDcId: Int32?, recordStartDate: Int32?, scheduleDate: Int32?, unmutedVideoCount: Int32?, unmutedVideoLimit: Int32, version: Int32)
|
||||
case groupCall(flags: Int32, id: Int64, accessHash: Int64, participantsCount: Int32, title: String?, streamDcId: Int32?, recordStartDate: Int32?, scheduleDate: Int32?, unmutedVideoCount: Int32?, unmutedVideoLimit: Int32, version: Int32, conferenceFromCall: Int64?)
|
||||
case groupCallDiscarded(id: Int64, accessHash: Int64, duration: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version):
|
||||
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version, let conferenceFromCall):
|
||||
if boxed {
|
||||
buffer.appendInt32(-711498484)
|
||||
buffer.appendInt32(-839330845)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
@ -1002,6 +1002,7 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(unmutedVideoCount!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(unmutedVideoLimit, buffer: buffer, boxed: false)
|
||||
serializeInt32(version, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 14) != 0 {serializeInt64(conferenceFromCall!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .groupCallDiscarded(let id, let accessHash, let duration):
|
||||
if boxed {
|
||||
@ -1016,8 +1017,8 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version):
|
||||
return ("groupCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("participantsCount", participantsCount as Any), ("title", title as Any), ("streamDcId", streamDcId as Any), ("recordStartDate", recordStartDate as Any), ("scheduleDate", scheduleDate as Any), ("unmutedVideoCount", unmutedVideoCount as Any), ("unmutedVideoLimit", unmutedVideoLimit as Any), ("version", version as Any)])
|
||||
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version, let conferenceFromCall):
|
||||
return ("groupCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("participantsCount", participantsCount as Any), ("title", title as Any), ("streamDcId", streamDcId as Any), ("recordStartDate", recordStartDate as Any), ("scheduleDate", scheduleDate as Any), ("unmutedVideoCount", unmutedVideoCount as Any), ("unmutedVideoLimit", unmutedVideoLimit as Any), ("version", version as Any), ("conferenceFromCall", conferenceFromCall as Any)])
|
||||
case .groupCallDiscarded(let id, let accessHash, let duration):
|
||||
return ("groupCallDiscarded", [("id", id as Any), ("accessHash", accessHash as Any), ("duration", duration as Any)])
|
||||
}
|
||||
@ -1046,6 +1047,8 @@ public extension Api {
|
||||
_10 = reader.readInt32()
|
||||
var _11: Int32?
|
||||
_11 = reader.readInt32()
|
||||
var _12: Int64?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {_12 = reader.readInt64() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -1057,8 +1060,9 @@ public extension Api {
|
||||
let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
let _c11 = _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, title: _5, streamDcId: _6, recordStartDate: _7, scheduleDate: _8, unmutedVideoCount: _9, unmutedVideoLimit: _10!, version: _11!)
|
||||
let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||
return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, title: _5, streamDcId: _6, recordStartDate: _7, scheduleDate: _8, unmutedVideoCount: _9, unmutedVideoLimit: _10!, version: _11!, conferenceFromCall: _12)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -71,7 +71,7 @@ public final class CallController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
public var peerId: EnginePeer.Id {
|
||||
public var peerId: EnginePeer.Id? {
|
||||
switch self {
|
||||
case let .call(call):
|
||||
return call.peerId
|
||||
@ -597,16 +597,27 @@ public final class CallController: ViewController {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
let callPeerView: Signal<PeerView?, NoError>
|
||||
if let peerId = self.call.peerId {
|
||||
callPeerView = self.account.postbox.peerView(id: peerId) |> map(Optional.init)
|
||||
} else {
|
||||
callPeerView = .single(nil)
|
||||
}
|
||||
|
||||
self.peerDisposable = (combineLatest(queue: .mainQueue(),
|
||||
self.account.postbox.peerView(id: self.account.peerId) |> take(1),
|
||||
self.account.postbox.peerView(id: self.call.peerId),
|
||||
callPeerView,
|
||||
self.sharedContext.activeAccountsWithInfo |> take(1)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] accountView, view, activeAccountsWithInfo in
|
||||
if let strongSelf = self {
|
||||
if let accountPeer = accountView.peers[accountView.peerId], let peer = view.peers[view.peerId] {
|
||||
strongSelf.peer = peer
|
||||
strongSelf.controllerNode.updatePeer(accountPeer: accountPeer, peer: peer, hasOther: activeAccountsWithInfo.accounts.count > 1)
|
||||
if let view {
|
||||
if let accountPeer = accountView.peers[accountView.peerId], let peer = view.peers[view.peerId] {
|
||||
strongSelf.peer = peer
|
||||
strongSelf.controllerNode.updatePeer(accountPeer: accountPeer, peer: peer, hasOther: activeAccountsWithInfo.accounts.count > 1)
|
||||
strongSelf.isDataReady.set(.single(true))
|
||||
}
|
||||
} else {
|
||||
strongSelf.isDataReady.set(.single(true))
|
||||
}
|
||||
}
|
||||
|
@ -323,16 +323,26 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
|
||||
strongSelf.update()
|
||||
}
|
||||
}))
|
||||
let callPeerView: Signal<PeerView?, NoError>
|
||||
if let peerId = call.peerId {
|
||||
callPeerView = account.postbox.peerView(id: peerId) |> map(Optional.init)
|
||||
} else {
|
||||
callPeerView = .single(nil)
|
||||
}
|
||||
self.stateDisposable.set(
|
||||
(combineLatest(
|
||||
account.postbox.peerView(id: call.peerId),
|
||||
callPeerView,
|
||||
call.summaryState,
|
||||
call.isMuted,
|
||||
call.members
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] view, state, isMuted, members in
|
||||
if let strongSelf = self {
|
||||
strongSelf.currentPeer = view.peers[view.peerId]
|
||||
if let view {
|
||||
strongSelf.currentPeer = view.peers[view.peerId]
|
||||
} else {
|
||||
strongSelf.currentPeer = nil
|
||||
}
|
||||
strongSelf.currentGroupCallState = state
|
||||
strongSelf.currentMembers = members
|
||||
|
||||
|
@ -112,7 +112,12 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||
strongSelf.updated(transition: .immediate)
|
||||
})
|
||||
|
||||
let callPeer = call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: call.peerId))
|
||||
let callPeer: Signal<EnginePeer?, NoError>
|
||||
if let peerId = call.peerId {
|
||||
callPeer = call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
} else {
|
||||
callPeer = .single(nil)
|
||||
}
|
||||
|
||||
self.infoDisposable = (combineLatest(queue: .mainQueue(), call.state, call.members, callPeer)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state, members, callPeer in
|
||||
@ -447,7 +452,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
var topLeftButton: AnyComponent<Empty>?
|
||||
|
||||
if context.state.canManageCall {
|
||||
if context.state.canManageCall, let peerId = context.component.call.peerId {
|
||||
let whiteColor = UIColor(white: 1.0, alpha: 1.0)
|
||||
topLeftButton = AnyComponent(Button(
|
||||
content: AnyComponent(ZStack([
|
||||
@ -542,20 +547,6 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||
|
||||
let _ = text
|
||||
let _ = controller
|
||||
|
||||
/*strongSelf.presentUndoOverlay(content: .forward(savedMessages: true, text: text), action: { [weak self] value in
|
||||
if case .info = value, let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
||||
let context = strongSelf.context
|
||||
strongSelf.controller?.dismiss(completion: {
|
||||
Queue.mainQueue().justDispatch {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(context.account.peerId), keepStack: .always, purposefulAction: {}, peekData: nil))
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})*/
|
||||
})])
|
||||
controller.present(alertController, in: .window(.root))
|
||||
|
||||
@ -604,7 +595,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
let credentialsPromise = Promise<GroupCallStreamCredentials>()
|
||||
credentialsPromise.set(call.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: call.peerId, revokePreviousCredentials: false) |> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in return .never() })
|
||||
credentialsPromise.set(call.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: peerId, revokePreviousCredentials: false) |> `catch` { _ -> Signal<GroupCallStreamCredentials, NoError> in return .never() })
|
||||
|
||||
items.append(.action(ContextMenuActionItem(id: nil, text: presentationData.strings.LiveStream_ViewCredentials, textColor: .primary, textLayout: .singleLine, textFont: .regular, badge: nil, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor, backgroundColor: nil)
|
||||
@ -613,7 +604,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||
return
|
||||
}
|
||||
|
||||
controller.push(CreateExternalMediaStreamScreen(context: call.accountContext, peerId: call.peerId, credentialsPromise: credentialsPromise, mode: .view))
|
||||
controller.push(CreateExternalMediaStreamScreen(context: call.accountContext, peerId: peerId, credentialsPromise: credentialsPromise, mode: .view))
|
||||
|
||||
a(.default)
|
||||
})))
|
||||
@ -663,38 +654,6 @@ public final class MediaStreamComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData.withUpdated(theme: defaultDarkPresentationTheme), source: .reference(ReferenceContentSource(sourceView: anchorView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
|
||||
/*contextController.passthroughTouchEvent = { sourceView, point in
|
||||
guard let strongSelf = self else {
|
||||
return .ignore
|
||||
}
|
||||
|
||||
let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil)
|
||||
guard let localResult = strongSelf.hitTest(localPoint, with: nil) else {
|
||||
return .dismiss(consume: true, result: nil)
|
||||
}
|
||||
|
||||
var testView: UIView? = localResult
|
||||
while true {
|
||||
if let testViewValue = testView {
|
||||
if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton {
|
||||
node.isUserInteractionEnabled = false
|
||||
DispatchQueue.main.async {
|
||||
node.isUserInteractionEnabled = true
|
||||
}
|
||||
return .dismiss(consume: false, result: nil)
|
||||
} else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode {
|
||||
node.brieflyDisableTouchActions()
|
||||
return .dismiss(consume: false, result: nil)
|
||||
} else {
|
||||
testView = testViewValue.superview
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return .dismiss(consume: true, result: nil)
|
||||
}*/
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
).minSize(CGSize(width: 44.0, height: 44.0)).tagged(moreButtonTag))
|
||||
@ -1172,10 +1131,13 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let peerId = strongSelf.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.call.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: strongSelf.call.peerId)
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: peerId)
|
||||
)
|
||||
|> map { peer, exportedInvitation -> GroupCallInviteLinks? in
|
||||
if let inviteLinks = inviteLinks {
|
||||
@ -1213,7 +1175,11 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
||||
}
|
||||
let _ = formatSendTitle
|
||||
|
||||
let _ = (combineLatest(queue: .mainQueue(), self.context.account.postbox.loadedPeerWithId(self.call.peerId), self.call.state |> take(1))
|
||||
guard let peerId = self.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (combineLatest(queue: .mainQueue(), self.context.account.postbox.loadedPeerWithId(peerId), self.call.state |> take(1))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer, callState in
|
||||
if let strongSelf = self {
|
||||
var inviteLinks = inviteLinks
|
||||
|
@ -199,7 +199,7 @@ final class MediaStreamVideoComponent: Component {
|
||||
if isStalled {
|
||||
guard let component = self.component else { return }
|
||||
|
||||
if let frameView = lastFrame[component.call.peerId.id.description] {
|
||||
if let peerId = component.call.peerId, let frameView = lastFrame[peerId.id.description] {
|
||||
frameView.removeFromSuperview()
|
||||
placeholderView.subviews.forEach { $0.removeFromSuperview() }
|
||||
placeholderView.addSubview(frameView)
|
||||
@ -469,11 +469,11 @@ final class MediaStreamVideoComponent: Component {
|
||||
}
|
||||
|
||||
if let videoView = self.videoView {
|
||||
if videoView.bounds.size.width > 0,
|
||||
if let peerId = component.call.peerId, videoView.bounds.size.width > 0,
|
||||
videoView.alpha > 0,
|
||||
self.hadVideo,
|
||||
let snapshot = videoView.snapshotView(afterScreenUpdates: false) ?? videoView.snapshotView(afterScreenUpdates: true) {
|
||||
lastFrame[component.call.peerId.id.description] = snapshot
|
||||
lastFrame[peerId.id.description] = snapshot
|
||||
}
|
||||
|
||||
var aspect = videoView.getAspect()
|
||||
@ -696,7 +696,7 @@ final class MediaStreamVideoComponent: Component {
|
||||
presentationParent.addSubview(presentation)
|
||||
presentation.frame = presentationParent.convert(videoView.frame, from: self)
|
||||
|
||||
if let callId = self.component?.call.peerId.id.description {
|
||||
if let callId = self.component?.call.peerId?.id.description {
|
||||
lastFrame[callId] = presentation
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
public let preferredVideoCodec: String?
|
||||
public let peer: EnginePeer?
|
||||
|
||||
private let isExpectedToBeConference: Bool
|
||||
|
||||
private let serializedData: String?
|
||||
private let dataSaving: VoiceCallDataSaving
|
||||
private let proxyServer: ProxyServerSettings?
|
||||
@ -128,6 +130,12 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
private let screencastAudioDataDisposable = MetaDisposable()
|
||||
private let screencastStateDisposable = MetaDisposable()
|
||||
|
||||
private var conferenceCall: PresentationGroupCallImpl?
|
||||
private var conferenceCallDisposable: Disposable?
|
||||
|
||||
private var localVideoEndpointId: String?
|
||||
private var remoteVideoEndpointId: String?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
@ -194,6 +202,8 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.currentNetworkType = currentNetworkType
|
||||
self.updatedNetworkType = updatedNetworkType
|
||||
|
||||
self.isExpectedToBeConference = self.context.sharedContext.immediateExperimentalUISettings.conferenceCalls
|
||||
|
||||
var didReceiveAudioOutputs = false
|
||||
|
||||
var callSessionState: Signal<CallSession, NoError> = .complete()
|
||||
@ -269,15 +279,6 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
let audioSessionActive: Signal<Bool, NoError>
|
||||
if let callKitIntegration = strongSelf.callKitIntegration {
|
||||
audioSessionActive = callKitIntegration.audioSessionActive
|
||||
/*|> filter { $0 }
|
||||
|> timeout(2.0, queue: Queue.mainQueue(), alternate: Signal { subscriber in
|
||||
if let strongSelf = self, let _ = strongSelf.audioSessionControl {
|
||||
//audioSessionControl.activate({ _ in })
|
||||
}
|
||||
subscriber.putNext(true)
|
||||
subscriber.putCompletion()
|
||||
return EmptyDisposable
|
||||
})*/
|
||||
} else {
|
||||
audioSessionControl.activate({ _ in })
|
||||
audioSessionActive = .single(true)
|
||||
@ -328,6 +329,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.screencastFramesDisposable.dispose()
|
||||
self.screencastAudioDataDisposable.dispose()
|
||||
self.screencastStateDisposable.dispose()
|
||||
self.conferenceCallDisposable?.dispose()
|
||||
|
||||
if let dropCallKitCallTimer = self.dropCallKitCallTimer {
|
||||
dropCallKitCallTimer.invalidate()
|
||||
@ -383,7 +385,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
print("updateSessionState \(sessionState.state) \(audioSessionControl != nil)")
|
||||
}
|
||||
|
||||
let presentationState: PresentationCallState?
|
||||
var presentationState: PresentationCallState?
|
||||
|
||||
var wasActive = false
|
||||
var wasTerminated = false
|
||||
@ -521,7 +523,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||
case let .requesting(ringing):
|
||||
presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||
case let .active(_, _, keyVisualHash, _, _, _, _, _):
|
||||
case let .active(_, _, keyVisualHash, _, _, _, _, _, _):
|
||||
self.callWasActive = true
|
||||
if let callContextState = callContextState {
|
||||
switch callContextState.state {
|
||||
@ -549,7 +551,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
}
|
||||
presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||
}
|
||||
} else {
|
||||
} else if !self.isExpectedToBeConference {
|
||||
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||
}
|
||||
}
|
||||
@ -557,80 +559,231 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
switch sessionState.state {
|
||||
case .requesting:
|
||||
if let _ = audioSessionControl {
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
if self.isExpectedToBeConference {
|
||||
} else {
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
}
|
||||
}
|
||||
case let .active(id, key, _, connections, maxLayer, version, customParameters, allowsP2P):
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
||||
let logName = "\(id.id)_\(id.accessHash)"
|
||||
|
||||
let updatedConnections = connections
|
||||
case let .active(id, key, _, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
|
||||
if let conferenceCall, self.conferenceCallDisposable == nil {
|
||||
presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||
|
||||
let ongoingContext = OngoingCallContext(account: self.context.account, callSessionManager: self.callSessionManager, callId: id, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: updatedConnections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec, audioDevice: self.sharedAudioDevice)
|
||||
self.ongoingContext = ongoingContext
|
||||
ongoingContext.setIsMuted(self.isMutedValue)
|
||||
if let requestedVideoAspect = self.requestedVideoAspect {
|
||||
ongoingContext.setRequestedVideoAspect(requestedVideoAspect)
|
||||
}
|
||||
|
||||
self.debugInfoValue.set(ongoingContext.debugInfo())
|
||||
|
||||
self.ongoingContextStateDisposable = (ongoingContext.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] contextState in
|
||||
if let strongSelf = self {
|
||||
if let sessionState = strongSelf.sessionState {
|
||||
strongSelf.updateSessionState(sessionState: sessionState, callContextState: contextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl)
|
||||
} else {
|
||||
strongSelf.callContextState = contextState
|
||||
self.conferenceCallDisposable = (self.context.engine.calls.getCurrentGroupCall(callId: conferenceCall.id, accessHash: conferenceCall.accessHash)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
|
||||
guard let self, let result else {
|
||||
return
|
||||
}
|
||||
|
||||
let conferenceCall = PresentationGroupCallImpl(
|
||||
accountContext: self.context,
|
||||
audioSession: self.audioSession,
|
||||
callKitIntegration: self.callKitIntegration,
|
||||
getDeviceAccessData: self.getDeviceAccessData,
|
||||
initialCall: EngineGroupCallDescription(
|
||||
id: result.info.id,
|
||||
accessHash: result.info.accessHash,
|
||||
title: nil,
|
||||
scheduleTimestamp: nil,
|
||||
subscribedToScheduled: false,
|
||||
isStream: false
|
||||
),
|
||||
internalId: CallSessionInternalId(),
|
||||
peerId: nil,
|
||||
isChannel: false,
|
||||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: false,
|
||||
encryptionKey: key
|
||||
)
|
||||
self.conferenceCall = conferenceCall
|
||||
|
||||
conferenceCall.setIsMuted(action: self.isMutedValue ? .muted(isPushToTalkActive: false) : .unmuted)
|
||||
|
||||
let accountPeerId = conferenceCall.account.peerId
|
||||
let videoEndpoints: Signal<(local: String?, remote: PresentationGroupCallRequestedVideo?), NoError> = conferenceCall.members
|
||||
|> map { members -> (local: String?, remote: PresentationGroupCallRequestedVideo?) in
|
||||
guard let members else {
|
||||
return (nil, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.audioLevelDisposable = (ongoingContext.audioLevel
|
||||
|> deliverOnMainQueue).start(next: { [weak self] level in
|
||||
if let strongSelf = self {
|
||||
strongSelf.audioLevelPromise.set(level)
|
||||
}
|
||||
})
|
||||
|
||||
func batteryLevelIsLowSignal() -> Signal<Bool, NoError> {
|
||||
return Signal { subscriber in
|
||||
let device = UIDevice.current
|
||||
device.isBatteryMonitoringEnabled = true
|
||||
|
||||
var previousBatteryLevelIsLow = false
|
||||
let timer = SwiftSignalKit.Timer(timeout: 30.0, repeat: true, completion: {
|
||||
let batteryLevelIsLow = device.batteryLevel >= 0.0 && device.batteryLevel < 0.1 && device.batteryState != .charging
|
||||
if batteryLevelIsLow != previousBatteryLevelIsLow {
|
||||
previousBatteryLevelIsLow = batteryLevelIsLow
|
||||
subscriber.putNext(batteryLevelIsLow)
|
||||
var local: String?
|
||||
var remote: PresentationGroupCallRequestedVideo?
|
||||
for participant in members.participants {
|
||||
if let video = participant.requestedPresentationVideoChannel(minQuality: .thumbnail, maxQuality: .full) ?? participant.requestedVideoChannel(minQuality: .thumbnail, maxQuality: .full) {
|
||||
if participant.peer.id == accountPeerId {
|
||||
local = video.endpointId
|
||||
} else {
|
||||
if remote == nil {
|
||||
remote = video
|
||||
}
|
||||
}
|
||||
}
|
||||
}, queue: Queue.mainQueue())
|
||||
timer.start()
|
||||
|
||||
return ActionDisposable {
|
||||
device.isBatteryMonitoringEnabled = false
|
||||
timer.invalidate()
|
||||
}
|
||||
return (local, remote)
|
||||
}
|
||||
}
|
||||
|
||||
self.batteryLevelDisposable = (batteryLevelIsLowSignal()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] batteryLevelIsLow in
|
||||
if let strongSelf = self, let ongoingContext = strongSelf.ongoingContext {
|
||||
ongoingContext.setIsLowBatteryLevel(batteryLevelIsLow)
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
return lhs == rhs
|
||||
})
|
||||
|
||||
var startTimestamp: Double?
|
||||
self.ongoingContextStateDisposable = (combineLatest(queue: .mainQueue(),
|
||||
conferenceCall.state,
|
||||
videoEndpoints
|
||||
)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] callState, videoEndpoints in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let mappedState: PresentationCallState.State
|
||||
switch callState.networkState {
|
||||
case .connecting:
|
||||
mappedState = .connecting(nil)
|
||||
case .connected:
|
||||
let timestamp = startTimestamp ?? CFAbsoluteTimeGetCurrent()
|
||||
startTimestamp = timestamp
|
||||
mappedState = .active(timestamp, nil, Data())
|
||||
}
|
||||
|
||||
var mappedLocalVideoState: PresentationCallState.VideoState = .inactive
|
||||
var mappedRemoteVideoState: PresentationCallState.RemoteVideoState = .inactive
|
||||
|
||||
if let local = videoEndpoints.local {
|
||||
mappedLocalVideoState = .active(isScreencast: false, endpointId: local)
|
||||
}
|
||||
if let remote = videoEndpoints.remote {
|
||||
mappedRemoteVideoState = .active(endpointId: remote.endpointId)
|
||||
}
|
||||
|
||||
self.localVideoEndpointId = videoEndpoints.local
|
||||
self.remoteVideoEndpointId = videoEndpoints.remote?.endpointId
|
||||
|
||||
if let conferenceCall = self.conferenceCall {
|
||||
var requestedVideo: [PresentationGroupCallRequestedVideo] = []
|
||||
if let remote = videoEndpoints.remote {
|
||||
requestedVideo.append(remote)
|
||||
}
|
||||
conferenceCall.setRequestedVideoList(items: requestedVideo)
|
||||
}
|
||||
|
||||
self.statePromise.set(PresentationCallState(
|
||||
state: mappedState,
|
||||
videoState: mappedLocalVideoState,
|
||||
remoteVideoState: mappedRemoteVideoState,
|
||||
remoteAudioState: .active,
|
||||
remoteBatteryLevel: .normal
|
||||
))
|
||||
})
|
||||
|
||||
var audioLevelId: UInt32?
|
||||
let audioLevel = conferenceCall.audioLevels |> map { audioLevels -> Float in
|
||||
var result: Float = 0
|
||||
for item in audioLevels {
|
||||
if let audioLevelId {
|
||||
if item.1 == audioLevelId {
|
||||
result = item.2
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if item.1 != 0 {
|
||||
audioLevelId = item.1
|
||||
result = item.2
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
self.audioLevelDisposable = (audioLevel
|
||||
|> deliverOnMainQueue).start(next: { [weak self] level in
|
||||
if let strongSelf = self {
|
||||
strongSelf.audioLevelPromise.set(level)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
if conferenceCall == nil, self.isExpectedToBeConference {
|
||||
self.createConferenceIfPossible()
|
||||
}
|
||||
|
||||
if self.isExpectedToBeConference {
|
||||
if sessionState.isOutgoing {
|
||||
self.callKitIntegration?.reportOutgoingCallConnected(uuid: sessionState.id, at: Date())
|
||||
}
|
||||
} else {
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
||||
let logName = "\(id.id)_\(id.accessHash)"
|
||||
|
||||
let updatedConnections = connections
|
||||
|
||||
let ongoingContext = OngoingCallContext(account: self.context.account, callSessionManager: self.callSessionManager, callId: id, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: updatedConnections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec, audioDevice: self.sharedAudioDevice)
|
||||
self.ongoingContext = ongoingContext
|
||||
ongoingContext.setIsMuted(self.isMutedValue)
|
||||
if let requestedVideoAspect = self.requestedVideoAspect {
|
||||
ongoingContext.setRequestedVideoAspect(requestedVideoAspect)
|
||||
}
|
||||
|
||||
self.debugInfoValue.set(ongoingContext.debugInfo())
|
||||
|
||||
self.ongoingContextStateDisposable = (ongoingContext.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] contextState in
|
||||
if let strongSelf = self {
|
||||
if let sessionState = strongSelf.sessionState {
|
||||
strongSelf.updateSessionState(sessionState: sessionState, callContextState: contextState, reception: strongSelf.reception, audioSessionControl: strongSelf.audioSessionControl)
|
||||
} else {
|
||||
strongSelf.callContextState = contextState
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.audioLevelDisposable = (ongoingContext.audioLevel
|
||||
|> deliverOnMainQueue).start(next: { [weak self] level in
|
||||
if let strongSelf = self {
|
||||
strongSelf.audioLevelPromise.set(level)
|
||||
}
|
||||
})
|
||||
|
||||
func batteryLevelIsLowSignal() -> Signal<Bool, NoError> {
|
||||
return Signal { subscriber in
|
||||
let device = UIDevice.current
|
||||
device.isBatteryMonitoringEnabled = true
|
||||
|
||||
var previousBatteryLevelIsLow = false
|
||||
let timer = SwiftSignalKit.Timer(timeout: 30.0, repeat: true, completion: {
|
||||
let batteryLevelIsLow = device.batteryLevel >= 0.0 && device.batteryLevel < 0.1 && device.batteryState != .charging
|
||||
if batteryLevelIsLow != previousBatteryLevelIsLow {
|
||||
previousBatteryLevelIsLow = batteryLevelIsLow
|
||||
subscriber.putNext(batteryLevelIsLow)
|
||||
}
|
||||
}, queue: Queue.mainQueue())
|
||||
timer.start()
|
||||
|
||||
return ActionDisposable {
|
||||
device.isBatteryMonitoringEnabled = false
|
||||
timer.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.batteryLevelDisposable = (batteryLevelIsLowSignal()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] batteryLevelIsLow in
|
||||
if let strongSelf = self, let ongoingContext = strongSelf.ongoingContext {
|
||||
ongoingContext.setIsLowBatteryLevel(batteryLevelIsLow)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
case let .terminated(_, _, options):
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
if self.isExpectedToBeConference {
|
||||
} else {
|
||||
self.audioSessionShouldBeActive.set(true)
|
||||
}
|
||||
if wasActive {
|
||||
let debugLogValue = Promise<String?>()
|
||||
self.ongoingContext?.stop(sendDebugLogs: options.contains(.sendDebugLogs), debugLogValue: debugLogValue)
|
||||
let _ = self.conferenceCall?.leave(terminateIfPossible: false).start()
|
||||
}
|
||||
case .dropping:
|
||||
break
|
||||
@ -639,6 +792,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
if wasActive {
|
||||
let debugLogValue = Promise<String?>()
|
||||
self.ongoingContext?.stop(debugLogValue: debugLogValue)
|
||||
let _ = self.conferenceCall?.leave(terminateIfPossible: false).start()
|
||||
}
|
||||
}
|
||||
var terminating = false
|
||||
@ -672,12 +826,64 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.callKitIntegration?.dropCall(uuid: self.internalId)
|
||||
}
|
||||
}
|
||||
if let presentationState = presentationState {
|
||||
if let presentationState {
|
||||
self.statePromise.set(presentationState)
|
||||
self.updateTone(presentationState, callContextState: callContextState, previous: previous)
|
||||
}
|
||||
}
|
||||
|
||||
private func requestMediaChannelDescriptions(ssrcs: Set<UInt32>, completion: @escaping ([OngoingGroupCallContext.MediaChannelDescription]) -> Void) -> Disposable {
|
||||
/*func extractMediaChannelDescriptions(remainingSsrcs: inout Set<UInt32>, participants: [GroupCallParticipantsContext.Participant], into result: inout [OngoingGroupCallContext.MediaChannelDescription]) {
|
||||
for participant in participants {
|
||||
guard let audioSsrc = participant.ssrc else {
|
||||
continue
|
||||
}
|
||||
|
||||
if remainingSsrcs.contains(audioSsrc) {
|
||||
remainingSsrcs.remove(audioSsrc)
|
||||
|
||||
result.append(OngoingGroupCallContext.MediaChannelDescription(
|
||||
kind: .audio,
|
||||
audioSsrc: audioSsrc,
|
||||
videoDescription: nil
|
||||
))
|
||||
}
|
||||
|
||||
if let screencastSsrc = participant.presentationDescription?.audioSsrc {
|
||||
if remainingSsrcs.contains(screencastSsrc) {
|
||||
remainingSsrcs.remove(screencastSsrc)
|
||||
|
||||
result.append(OngoingGroupCallContext.MediaChannelDescription(
|
||||
kind: .audio,
|
||||
audioSsrc: screencastSsrc,
|
||||
videoDescription: nil
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var remainingSsrcs = ssrcs
|
||||
var result: [OngoingGroupCallContext.MediaChannelDescription] = []
|
||||
|
||||
if let membersValue = self.membersValue {
|
||||
extractMediaChannelDescriptions(remainingSsrcs: &remainingSsrcs, participants: membersValue.participants, into: &result)
|
||||
}
|
||||
|
||||
if !remainingSsrcs.isEmpty, let callInfo = self.internalState.callInfo {
|
||||
return (self.accountContext.engine.calls.getGroupCallParticipants(callId: callInfo.id, accessHash: callInfo.accessHash, offset: "", ssrcs: Array(remainingSsrcs), limit: 100, sortAscending: callInfo.sortAscending)
|
||||
|> deliverOnMainQueue).start(next: { state in
|
||||
extractMediaChannelDescriptions(remainingSsrcs: &remainingSsrcs, participants: state.participants, into: &result)
|
||||
|
||||
completion(result)
|
||||
})
|
||||
} else {
|
||||
completion(result)
|
||||
return EmptyDisposable
|
||||
}*/
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
private func updateTone(_ state: PresentationCallState, callContextState: OngoingCallContextState?, previous: CallSession?) {
|
||||
var tone: PresentationCallTone?
|
||||
if let callContextState = callContextState, case .reconnecting = callContextState.state {
|
||||
@ -784,6 +990,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
let debugLogValue = Promise<String?>()
|
||||
self.callSessionManager.drop(internalId: self.internalId, reason: .hangUp, debugLog: debugLogValue.get())
|
||||
self.ongoingContext?.stop(debugLogValue: debugLogValue)
|
||||
let _ = self.conferenceCall?.leave(terminateIfPossible: false).start()
|
||||
|
||||
return self.hungUpPromise.get()
|
||||
}
|
||||
@ -792,6 +999,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.callSessionManager.drop(internalId: self.internalId, reason: .busy, debugLog: .single(nil))
|
||||
let debugLog = Promise<String?>()
|
||||
self.ongoingContext?.stop(debugLogValue: debugLog)
|
||||
let _ = self.conferenceCall?.leave(terminateIfPossible: false).start()
|
||||
}
|
||||
|
||||
public func toggleIsMuted() {
|
||||
@ -802,6 +1010,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.isMutedValue = value
|
||||
self.isMutedPromise.set(self.isMutedValue)
|
||||
self.ongoingContext?.setIsMuted(self.isMutedValue)
|
||||
self.conferenceCall?.setIsMuted(action: self.isMutedValue ? .muted(isPushToTalkActive: false) : .unmuted)
|
||||
}
|
||||
|
||||
public func requestVideo() {
|
||||
@ -810,7 +1019,11 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.videoCapturer = videoCapturer
|
||||
}
|
||||
if let videoCapturer = self.videoCapturer {
|
||||
self.ongoingContext?.requestVideo(videoCapturer)
|
||||
if let ongoingContext = self.ongoingContext {
|
||||
ongoingContext.requestVideo(videoCapturer)
|
||||
} else if let conferenceCall = self.conferenceCall {
|
||||
conferenceCall.requestVideo(capturer: videoCapturer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -822,7 +1035,11 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
public func disableVideo() {
|
||||
if let _ = self.videoCapturer {
|
||||
self.videoCapturer = nil
|
||||
self.ongoingContext?.disableVideo()
|
||||
if let ongoingContext = self.ongoingContext {
|
||||
ongoingContext.disableVideo()
|
||||
} else if let conferenceCall = self.conferenceCall {
|
||||
conferenceCall.disableVideo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -864,7 +1081,11 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
|
||||
if let screencastCapturer = self.screencastCapturer {
|
||||
self.isScreencastActive = true
|
||||
self.ongoingContext?.requestVideo(screencastCapturer)
|
||||
if let ongoingContext = self.ongoingContext {
|
||||
ongoingContext.requestVideo(screencastCapturer)
|
||||
} else if let conferenceCall = self.conferenceCall {
|
||||
conferenceCall.requestVideo(capturer: screencastCapturer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -875,6 +1096,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
}
|
||||
self.isScreencastActive = false
|
||||
self.ongoingContext?.disableVideo()
|
||||
self.conferenceCall?.disableVideo()
|
||||
if reset {
|
||||
self.resetScreencastContext()
|
||||
}
|
||||
@ -913,7 +1135,13 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
|
||||
func video(isIncoming: Bool) -> Signal<OngoingGroupCallContext.VideoFrameData, NoError>? {
|
||||
if isIncoming {
|
||||
return self.ongoingContext?.video(isIncoming: isIncoming)
|
||||
if let ongoingContext = self.ongoingContext {
|
||||
return ongoingContext.video(isIncoming: isIncoming)
|
||||
} else if let conferenceCall = self.conferenceCall, let remoteVideoEndpointId = self.remoteVideoEndpointId {
|
||||
return conferenceCall.video(endpointId: remoteVideoEndpointId)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if let videoCapturer = self.videoCapturer {
|
||||
return videoCapturer.video()
|
||||
} else {
|
||||
@ -921,73 +1149,8 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
}
|
||||
}
|
||||
|
||||
public func makeIncomingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void) {
|
||||
self.ongoingContext?.makeIncomingVideoView(completion: { view in
|
||||
if let view = view {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
let updateIsEnabled = view.updateIsEnabled
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch view.getOrientation() {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
return mappedValue
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { value in
|
||||
updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
})
|
||||
public func createConferenceIfPossible() {
|
||||
self.callSessionManager.createConferenceIfNecessary(internalId: self.internalId)
|
||||
}
|
||||
|
||||
public func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void) {
|
||||
|
@ -702,7 +702,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
isChannel: isChannel,
|
||||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: false
|
||||
isStream: false,
|
||||
encryptionKey: nil
|
||||
)
|
||||
call.schedule(timestamp: timestamp)
|
||||
|
||||
@ -741,15 +742,16 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
isChannel: isChannel,
|
||||
invite: nil,
|
||||
joinAsPeerId: nil,
|
||||
isStream: false
|
||||
isStream: false,
|
||||
encryptionKey: nil
|
||||
)
|
||||
strongSelf.updateCurrentGroupCall(call)
|
||||
strongSelf.currentGroupCallPromise.set(.single(call))
|
||||
strongSelf.hasActiveGroupCallsPromise.set(true)
|
||||
strongSelf.removeCurrentGroupCallDisposable.set((call.canBeRemoved
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak call] value in
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak call] value in
|
||||
guard let strongSelf = self, let call = call else {
|
||||
return
|
||||
}
|
||||
@ -921,7 +923,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
isChannel: isChannel,
|
||||
invite: invite,
|
||||
joinAsPeerId: joinAsPeerId,
|
||||
isStream: initialCall.isStream ?? false
|
||||
isStream: initialCall.isStream ?? false,
|
||||
encryptionKey: nil
|
||||
)
|
||||
strongSelf.updateCurrentGroupCall(call)
|
||||
strongSelf.currentGroupCallPromise.set(.single(call))
|
||||
|
@ -91,41 +91,23 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
return self.panelDataPromise.get()
|
||||
}
|
||||
|
||||
public init(account: Account, engine: TelegramEngine, peerId: PeerId, isChannel: Bool, call: EngineGroupCallDescription) {
|
||||
public init(account: Account, engine: TelegramEngine, peerId: PeerId?, isChannel: Bool, call: EngineGroupCallDescription) {
|
||||
self.panelDataPromise.set(.single(nil))
|
||||
/*self.panelDataPromise.set(.single(GroupCallPanelData(
|
||||
peerId: peerId,
|
||||
isChannel: isChannel,
|
||||
info: GroupCallInfo(
|
||||
id: call.id,
|
||||
accessHash: call.accessHash,
|
||||
participantCount: 0,
|
||||
streamDcId: nil,
|
||||
title: call.title,
|
||||
scheduleTimestamp: call.scheduleTimestamp,
|
||||
subscribedToScheduled: call.subscribedToScheduled,
|
||||
recordingStartTimestamp: nil,
|
||||
sortAscending: true,
|
||||
defaultParticipantsAreMuted: nil,
|
||||
isVideoEnabled: false,
|
||||
unmutedVideoLimit: 0,
|
||||
isStream: call.isStream
|
||||
),
|
||||
topParticipants: [],
|
||||
participantCount: 0,
|
||||
activeSpeakers: Set(),
|
||||
groupCall: nil
|
||||
)))*/
|
||||
|
||||
let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
let peer: Signal<EnginePeer?, NoError>
|
||||
if let peerId {
|
||||
peer = engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
} else {
|
||||
peer = .single(nil)
|
||||
}
|
||||
self.disposable = (combineLatest(queue: .mainQueue(),
|
||||
state,
|
||||
engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
peer
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state, peer in
|
||||
guard let strongSelf = self, let state = state else {
|
||||
@ -141,34 +123,37 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
)
|
||||
|
||||
strongSelf.participantsContext = context
|
||||
strongSelf.panelDataPromise.set(combineLatest(queue: .mainQueue(),
|
||||
context.state,
|
||||
context.activeSpeakers
|
||||
)
|
||||
|> map { state, activeSpeakers -> GroupCallPanelData in
|
||||
var topParticipants: [GroupCallParticipantsContext.Participant] = []
|
||||
for participant in state.participants {
|
||||
if topParticipants.count >= 3 {
|
||||
break
|
||||
}
|
||||
topParticipants.append(participant)
|
||||
}
|
||||
|
||||
var isChannel = false
|
||||
if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info {
|
||||
isChannel = true
|
||||
}
|
||||
|
||||
return GroupCallPanelData(
|
||||
peerId: peerId,
|
||||
isChannel: isChannel,
|
||||
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit, isStream: state.isStream),
|
||||
topParticipants: topParticipants,
|
||||
participantCount: state.totalCount,
|
||||
activeSpeakers: activeSpeakers,
|
||||
groupCall: nil
|
||||
|
||||
if let peerId {
|
||||
strongSelf.panelDataPromise.set(combineLatest(queue: .mainQueue(),
|
||||
context.state,
|
||||
context.activeSpeakers
|
||||
)
|
||||
})
|
||||
|> map { state, activeSpeakers -> GroupCallPanelData in
|
||||
var topParticipants: [GroupCallParticipantsContext.Participant] = []
|
||||
for participant in state.participants {
|
||||
if topParticipants.count >= 3 {
|
||||
break
|
||||
}
|
||||
topParticipants.append(participant)
|
||||
}
|
||||
|
||||
var isChannel = false
|
||||
if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info {
|
||||
isChannel = true
|
||||
}
|
||||
|
||||
return GroupCallPanelData(
|
||||
peerId: peerId,
|
||||
isChannel: isChannel,
|
||||
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit, isStream: state.isStream, upgradedPrivateCallId: state.upgradedPrivateCallId),
|
||||
topParticipants: topParticipants,
|
||||
participantCount: state.totalCount,
|
||||
activeSpeakers: activeSpeakers,
|
||||
groupCall: nil
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -398,15 +383,6 @@ private extension CurrentImpl {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func makeIncomingVideoView(endpointId: String, requestClone: Bool, completion: @escaping (OngoingCallContextPresentationCallVideoView?, OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||
switch self {
|
||||
case let .call(callContext):
|
||||
callContext.makeIncomingVideoView(endpointId: endpointId, requestClone: requestClone, completion: completion)
|
||||
case .mediaStream, .externalMediaStream:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func video(endpointId: String) -> Signal<OngoingGroupCallContext.VideoFrameData, NoError> {
|
||||
switch self {
|
||||
@ -623,7 +599,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
private(set) var initialCall: EngineGroupCallDescription?
|
||||
public let internalId: CallSessionInternalId
|
||||
public let peerId: EnginePeer.Id
|
||||
public let peerId: EnginePeer.Id?
|
||||
private let isChannel: Bool
|
||||
private var invite: String?
|
||||
private var joinAsPeerId: EnginePeer.Id
|
||||
@ -865,6 +841,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
private var screencastStateDisposable: Disposable?
|
||||
|
||||
public let isStream: Bool
|
||||
private let encryptionKey: Data?
|
||||
|
||||
public var onMutedSpeechActivityDetected: ((Bool) -> Void)?
|
||||
|
||||
@ -875,11 +852,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void),
|
||||
initialCall: EngineGroupCallDescription?,
|
||||
internalId: CallSessionInternalId,
|
||||
peerId: EnginePeer.Id,
|
||||
peerId: EnginePeer.Id?,
|
||||
isChannel: Bool,
|
||||
invite: String?,
|
||||
joinAsPeerId: EnginePeer.Id?,
|
||||
isStream: Bool
|
||||
isStream: Bool,
|
||||
encryptionKey: Data?
|
||||
) {
|
||||
self.account = accountContext.account
|
||||
self.accountContext = accountContext
|
||||
@ -905,6 +883,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.hasVideo = false
|
||||
self.hasScreencast = false
|
||||
self.isStream = isStream
|
||||
self.encryptionKey = encryptionKey
|
||||
|
||||
var didReceiveAudioOutputs = false
|
||||
|
||||
@ -1080,7 +1059,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
)
|
||||
})
|
||||
|
||||
if let initialCall = initialCall, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
|
||||
if let initialCall = initialCall, let peerId, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
|
||||
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled, isStream: initialCall.isStream))
|
||||
}) {
|
||||
self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId)
|
||||
@ -1100,30 +1079,32 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
})
|
||||
|
||||
let _ = (self.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var canManageCall = false
|
||||
if let peer = peer as? TelegramGroup {
|
||||
if case .creator = peer.role {
|
||||
canManageCall = true
|
||||
} else if case let .admin(rights, _) = peer.role, rights.rights.contains(.canManageCalls) {
|
||||
canManageCall = true
|
||||
if let peerId {
|
||||
let _ = (self.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
} else if let peer = peer as? TelegramChannel {
|
||||
if peer.flags.contains(.isCreator) {
|
||||
canManageCall = true
|
||||
} else if (peer.adminRights?.rights.contains(.canManageCalls) == true) {
|
||||
canManageCall = true
|
||||
var canManageCall = false
|
||||
if let peer = peer as? TelegramGroup {
|
||||
if case .creator = peer.role {
|
||||
canManageCall = true
|
||||
} else if case let .admin(rights, _) = peer.role, rights.rights.contains(.canManageCalls) {
|
||||
canManageCall = true
|
||||
}
|
||||
} else if let peer = peer as? TelegramChannel {
|
||||
if peer.flags.contains(.isCreator) {
|
||||
canManageCall = true
|
||||
} else if (peer.adminRights?.rights.contains(.canManageCalls) == true) {
|
||||
canManageCall = true
|
||||
}
|
||||
strongSelf.peerUpdatesSubscription = strongSelf.accountContext.account.viewTracker.polledChannel(peerId: peer.id).start()
|
||||
}
|
||||
strongSelf.peerUpdatesSubscription = strongSelf.accountContext.account.viewTracker.polledChannel(peerId: peer.id).start()
|
||||
}
|
||||
var updatedValue = strongSelf.stateValue
|
||||
updatedValue.canManageCall = canManageCall
|
||||
strongSelf.stateValue = updatedValue
|
||||
})
|
||||
var updatedValue = strongSelf.stateValue
|
||||
updatedValue.canManageCall = canManageCall
|
||||
strongSelf.stateValue = updatedValue
|
||||
})
|
||||
}
|
||||
|
||||
if let _ = self.initialCall {
|
||||
self.requestCall(movingFromBroadcastToRtc: false)
|
||||
@ -1398,48 +1379,58 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
let accountContext = self.accountContext
|
||||
let peerId = self.peerId
|
||||
let rawAdminIds: Signal<Set<PeerId>, NoError>
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
rawAdminIds = Signal { subscriber in
|
||||
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: accountContext.engine, postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
|
||||
var peerIds = Set<PeerId>()
|
||||
for item in list.list {
|
||||
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
|
||||
peerIds.insert(item.peer.id)
|
||||
if let peerId {
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
rawAdminIds = Signal { subscriber in
|
||||
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: accountContext.engine, postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
|
||||
var peerIds = Set<PeerId>()
|
||||
for item in list.list {
|
||||
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
|
||||
peerIds.insert(item.peer.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
subscriber.putNext(peerIds)
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> runOn(.mainQueue())
|
||||
} else {
|
||||
rawAdminIds = accountContext.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId)
|
||||
)
|
||||
|> map { participants -> Set<PeerId> in
|
||||
guard case let .known(participants) = participants else {
|
||||
return Set()
|
||||
subscriber.putNext(peerIds)
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
return Set(participants.compactMap { item -> PeerId? in
|
||||
switch item {
|
||||
case .creator, .admin:
|
||||
return item.peerId
|
||||
default:
|
||||
return nil
|
||||
|> distinctUntilChanged
|
||||
|> runOn(.mainQueue())
|
||||
} else {
|
||||
rawAdminIds = accountContext.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId)
|
||||
)
|
||||
|> map { participants -> Set<PeerId> in
|
||||
guard case let .known(participants) = participants else {
|
||||
return Set()
|
||||
}
|
||||
})
|
||||
return Set(participants.compactMap { item -> PeerId? in
|
||||
switch item {
|
||||
case .creator, .admin:
|
||||
return item.peerId
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
} else {
|
||||
rawAdminIds = .single(Set())
|
||||
}
|
||||
|
||||
let peer: Signal<EnginePeer?, NoError>
|
||||
if let peerId {
|
||||
peer = accountContext.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
} else {
|
||||
peer = .single(nil)
|
||||
}
|
||||
let adminIds = combineLatest(queue: .mainQueue(),
|
||||
rawAdminIds,
|
||||
accountContext.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
peer
|
||||
)
|
||||
|> map { rawAdminIds, peer -> Set<PeerId> in
|
||||
var rawAdminIds = rawAdminIds
|
||||
if case let .channel(peer) = peer {
|
||||
if let peer, case let .channel(peer) = peer {
|
||||
if peer.hasPermission(.manageCalls) {
|
||||
rawAdminIds.insert(accountContext.account.peerId)
|
||||
} else {
|
||||
@ -1470,6 +1461,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
isVideoEnabled: callInfo.isVideoEnabled,
|
||||
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream,
|
||||
upgradedPrivateCallId: callInfo.upgradedPrivateCallId,
|
||||
version: 0
|
||||
),
|
||||
previousServiceState: nil
|
||||
@ -1496,11 +1488,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
let peerView: Signal<PeerView?, NoError>
|
||||
if let peerId {
|
||||
peerView = accountContext.account.postbox.peerView(id: peerId) |> map(Optional.init)
|
||||
} else {
|
||||
peerView = .single(nil)
|
||||
}
|
||||
self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
|
||||
participantsContext.state,
|
||||
adminIds,
|
||||
myPeerData,
|
||||
accountContext.account.postbox.peerView(id: peerId)
|
||||
peerView
|
||||
).start(next: { [weak self] state, adminIds, myPeerData, view in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -1562,7 +1560,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
strongSelf.stateValue.subscribedToScheduled = state.subscribedToScheduled
|
||||
strongSelf.stateValue.scheduleTimestamp = strongSelf.isScheduledStarted ? nil : state.scheduleTimestamp
|
||||
if state.scheduleTimestamp == nil && !strongSelf.isScheduledStarted {
|
||||
strongSelf.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true, defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted, isVideoEnabled: callInfo.isVideoEnabled, unmutedVideoLimit: callInfo.unmutedVideoLimit, isStream: callInfo.isStream)), audioSessionControl: strongSelf.audioSessionControl)
|
||||
strongSelf.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true, defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted, isVideoEnabled: callInfo.isVideoEnabled, unmutedVideoLimit: callInfo.unmutedVideoLimit, isStream: callInfo.isStream, upgradedPrivateCallId: callInfo.upgradedPrivateCallId)), audioSessionControl: strongSelf.audioSessionControl)
|
||||
} else {
|
||||
strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
||||
id: callInfo.id,
|
||||
@ -1577,7 +1575,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
upgradedPrivateCallId: callInfo.upgradedPrivateCallId
|
||||
))))
|
||||
|
||||
strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
@ -1672,6 +1671,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
if let data = appConfiguration.data, let value = data["voice_chat_send_bitrate"] as? Double {
|
||||
outgoingAudioBitrateKbit = Int32(value)
|
||||
}
|
||||
|
||||
var encryptionKey: Data?
|
||||
encryptionKey = self.encryptionKey
|
||||
|
||||
#if DEBUG
|
||||
if encryptionKey == nil {
|
||||
encryptionKey = Data(count: 256)
|
||||
}
|
||||
#endif
|
||||
|
||||
genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: self.audioSessionActive.get(), video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in
|
||||
let disposable = MetaDisposable()
|
||||
@ -1698,7 +1706,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
strongSelf.onMutedSpeechActivityDetected?(value)
|
||||
}
|
||||
}))
|
||||
}, encryptionKey: encryptionKey))
|
||||
}
|
||||
|
||||
self.genericCallContext = genericCallContext
|
||||
@ -1725,39 +1733,43 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
let peerAdminIds: Signal<[PeerId], NoError>
|
||||
let peerId = strongSelf.peerId
|
||||
if strongSelf.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
peerAdminIds = Signal { subscriber in
|
||||
let (disposable, _) = strongSelf.accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: strongSelf.accountContext.engine, postbox: strongSelf.accountContext.account.postbox, network: strongSelf.accountContext.account.network, accountPeerId: strongSelf.accountContext.account.peerId, peerId: peerId, updated: { list in
|
||||
var peerIds = Set<PeerId>()
|
||||
for item in list.list {
|
||||
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
|
||||
peerIds.insert(item.peer.id)
|
||||
if let peerId {
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
peerAdminIds = Signal { subscriber in
|
||||
let (disposable, _) = strongSelf.accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: strongSelf.accountContext.engine, postbox: strongSelf.accountContext.account.postbox, network: strongSelf.accountContext.account.network, accountPeerId: strongSelf.accountContext.account.peerId, peerId: peerId, updated: { list in
|
||||
var peerIds = Set<PeerId>()
|
||||
for item in list.list {
|
||||
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
|
||||
peerIds.insert(item.peer.id)
|
||||
}
|
||||
}
|
||||
subscriber.putNext(Array(peerIds))
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> runOn(.mainQueue())
|
||||
} else {
|
||||
peerAdminIds = strongSelf.accountContext.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId)
|
||||
)
|
||||
|> map { participants -> [EnginePeer.Id] in
|
||||
guard case let .known(participants) = participants else {
|
||||
return []
|
||||
}
|
||||
var result: [EnginePeer.Id] = []
|
||||
for participant in participants {
|
||||
if case .creator = participant {
|
||||
result.append(participant.peerId)
|
||||
} else if case .admin = participant {
|
||||
result.append(participant.peerId)
|
||||
}
|
||||
}
|
||||
subscriber.putNext(Array(peerIds))
|
||||
})
|
||||
return disposable
|
||||
return result
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> runOn(.mainQueue())
|
||||
} else {
|
||||
peerAdminIds = strongSelf.accountContext.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId)
|
||||
)
|
||||
|> map { participants -> [EnginePeer.Id] in
|
||||
guard case let .known(participants) = participants else {
|
||||
return []
|
||||
}
|
||||
var result: [EnginePeer.Id] = []
|
||||
for participant in participants {
|
||||
if case .creator = participant {
|
||||
result.append(participant.peerId)
|
||||
} else if case .admin = participant {
|
||||
result.append(participant.peerId)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
peerAdminIds = .single([])
|
||||
}
|
||||
|
||||
strongSelf.currentLocalSsrc = ssrc
|
||||
@ -1842,8 +1854,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})
|
||||
]), on: .root, blockInteraction: false, completion: {})
|
||||
} else if case .invalidJoinAsPeer = error {
|
||||
let peerId = strongSelf.peerId
|
||||
let _ = strongSelf.accountContext.engine.calls.clearCachedGroupCallDisplayAsAvailablePeers(peerId: peerId).start()
|
||||
if let peerId = strongSelf.peerId {
|
||||
let _ = strongSelf.accountContext.engine.calls.clearCachedGroupCallDisplayAsAvailablePeers(peerId: peerId).start()
|
||||
}
|
||||
}
|
||||
strongSelf.markAsCanBeRemoved()
|
||||
}))
|
||||
@ -1995,48 +2008,59 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
let accountContext = self.accountContext
|
||||
let peerId = self.peerId
|
||||
let rawAdminIds: Signal<Set<PeerId>, NoError>
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
rawAdminIds = Signal { subscriber in
|
||||
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: accountContext.engine, postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
|
||||
var peerIds = Set<PeerId>()
|
||||
for item in list.list {
|
||||
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
|
||||
peerIds.insert(item.peer.id)
|
||||
if let peerId {
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
rawAdminIds = Signal { subscriber in
|
||||
let (disposable, _) = accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: accountContext.engine, postbox: accountContext.account.postbox, network: accountContext.account.network, accountPeerId: accountContext.account.peerId, peerId: peerId, updated: { list in
|
||||
var peerIds = Set<PeerId>()
|
||||
for item in list.list {
|
||||
if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
|
||||
peerIds.insert(item.peer.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
subscriber.putNext(peerIds)
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> runOn(.mainQueue())
|
||||
} else {
|
||||
rawAdminIds = accountContext.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId)
|
||||
)
|
||||
|> map { participants -> Set<PeerId> in
|
||||
guard case let .known(participants) = participants else {
|
||||
return Set()
|
||||
subscriber.putNext(peerIds)
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
return Set(participants.compactMap { item -> PeerId? in
|
||||
switch item {
|
||||
case .creator, .admin:
|
||||
return item.peerId
|
||||
default:
|
||||
return nil
|
||||
|> distinctUntilChanged
|
||||
|> runOn(.mainQueue())
|
||||
} else {
|
||||
rawAdminIds = accountContext.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId)
|
||||
)
|
||||
|> map { participants -> Set<PeerId> in
|
||||
guard case let .known(participants) = participants else {
|
||||
return Set()
|
||||
}
|
||||
})
|
||||
return Set(participants.compactMap { item -> PeerId? in
|
||||
switch item {
|
||||
case .creator, .admin:
|
||||
return item.peerId
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
} else {
|
||||
rawAdminIds = .single(Set())
|
||||
}
|
||||
|
||||
let peer: Signal<EnginePeer?, NoError>
|
||||
if let peerId {
|
||||
peer = accountContext.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
} else {
|
||||
peer = .single(nil)
|
||||
}
|
||||
|
||||
let adminIds = combineLatest(queue: .mainQueue(),
|
||||
rawAdminIds,
|
||||
accountContext.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
peer
|
||||
)
|
||||
|> map { rawAdminIds, peer -> Set<PeerId> in
|
||||
var rawAdminIds = rawAdminIds
|
||||
if case let .channel(peer) = peer {
|
||||
if let peer, case let .channel(peer) = peer {
|
||||
if peer.hasPermission(.manageCalls) {
|
||||
rawAdminIds.insert(accountContext.account.peerId)
|
||||
} else {
|
||||
@ -2080,13 +2104,25 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
let chatPeer = self.accountContext.account.postbox.peerView(id: self.peerId)
|
||||
|> map { view -> Peer? in
|
||||
if let peer = peerViewMainPeer(view) {
|
||||
return peer
|
||||
} else {
|
||||
return nil
|
||||
let chatPeer: Signal<Peer?, NoError>
|
||||
if let peerId = self.peerId {
|
||||
chatPeer = self.accountContext.account.postbox.peerView(id: peerId)
|
||||
|> map { view -> Peer? in
|
||||
if let peer = peerViewMainPeer(view) {
|
||||
return peer
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chatPeer = .single(nil)
|
||||
}
|
||||
|
||||
let peerView: Signal<PeerView?, NoError>
|
||||
if let peerId {
|
||||
peerView = accountContext.account.postbox.peerView(id: peerId) |> map(Optional.init)
|
||||
} else {
|
||||
peerView = .single(nil)
|
||||
}
|
||||
|
||||
self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
|
||||
@ -2096,7 +2132,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
adminIds,
|
||||
myPeer,
|
||||
chatPeer,
|
||||
accountContext.account.postbox.peerView(id: peerId),
|
||||
peerView,
|
||||
self.isReconnectingAsSpeakerPromise.get()
|
||||
).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeerAndCachedData, chatPeer, view, isReconnectingAsSpeaker in
|
||||
guard let strongSelf = self else {
|
||||
@ -2180,30 +2216,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
||||
}
|
||||
}
|
||||
|
||||
/*if let chatPeer = chatPeer, !participants.contains(where: { $0.peer.id == chatPeer.id }) {
|
||||
participants.append(GroupCallParticipantsContext.Participant(
|
||||
peer: chatPeer,
|
||||
ssrc: 100,
|
||||
videoDescription: GroupCallParticipantsContext.Participant.VideoDescription(
|
||||
endpointId: "unified",
|
||||
ssrcGroups: [],
|
||||
audioSsrc: 100,
|
||||
isPaused: false
|
||||
),
|
||||
presentationDescription: nil,
|
||||
joinTimestamp: strongSelf.temporaryJoinTimestamp,
|
||||
raiseHandRating: nil,
|
||||
hasRaiseHand: false,
|
||||
activityTimestamp: nil,
|
||||
activityRank: nil,
|
||||
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: false),
|
||||
volume: nil,
|
||||
about: nil,
|
||||
joinedVideo: false
|
||||
))
|
||||
participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
||||
}*/
|
||||
|
||||
var otherParticipantsWithVideo = 0
|
||||
var videoWatchingParticipants = 0
|
||||
@ -2259,7 +2271,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
let title: String?
|
||||
if let voiceChatTitle = strongSelf.stateValue.title {
|
||||
title = voiceChatTitle
|
||||
} else if let peer = peerViewMainPeer(view) {
|
||||
} else if let view, let peer = peerViewMainPeer(view) {
|
||||
title = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
} else {
|
||||
title = nil
|
||||
@ -2364,7 +2376,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
upgradedPrivateCallId: callInfo.upgradedPrivateCallId
|
||||
))))
|
||||
|
||||
strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
@ -2723,6 +2736,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
guard self.schedulePending else {
|
||||
return
|
||||
}
|
||||
guard let peerId = self.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
self.schedulePending = false
|
||||
self.stateValue.scheduleTimestamp = timestamp
|
||||
@ -2733,7 +2749,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
activeSpeakers: Set()
|
||||
)))
|
||||
|
||||
self.startDisposable.set((self.accountContext.engine.calls.createGroupCall(peerId: self.peerId, title: nil, scheduleDate: timestamp, isExternalStream: false)
|
||||
self.startDisposable.set((self.accountContext.engine.calls.createGroupCall(peerId: peerId, title: nil, scheduleDate: timestamp, isExternalStream: false)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] callInfo in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -2751,11 +2767,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
guard case let .active(callInfo) = self.internalState else {
|
||||
return
|
||||
}
|
||||
guard let peerId = self.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
self.isScheduledStarted = true
|
||||
self.stateValue.scheduleTimestamp = nil
|
||||
|
||||
self.startDisposable.set((self.accountContext.engine.calls.startScheduledGroupCall(peerId: self.peerId, callId: callInfo.id, accessHash: callInfo.accessHash)
|
||||
self.startDisposable.set((self.accountContext.engine.calls.startScheduledGroupCall(peerId: peerId, callId: callInfo.id, accessHash: callInfo.accessHash)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] callInfo in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -3002,7 +3021,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
self.hasScreencast = true
|
||||
|
||||
let screencastCallContext = OngoingGroupCallContext(audioSessionActive: .single(true), video: self.screencastCapturer, requestMediaChannelDescriptions: { _, _ in EmptyDisposable }, rejoinNeeded: { }, outgoingAudioBitrateKbit: nil, videoContentType: .screencast, enableNoiseSuppression: false, disableAudioInput: true, enableSystemMute: false, preferX264: false, logPath: "", onMutedSpeechActivityDetected: { _ in })
|
||||
let screencastCallContext = OngoingGroupCallContext(audioSessionActive: .single(true), video: self.screencastCapturer, requestMediaChannelDescriptions: { _, _ in EmptyDisposable }, rejoinNeeded: { }, outgoingAudioBitrateKbit: nil, videoContentType: .screencast, enableNoiseSuppression: false, disableAudioInput: true, enableSystemMute: false, preferX264: false, logPath: "", onMutedSpeechActivityDetected: { _ in }, encryptionKey: nil)
|
||||
self.screencastCallContext = screencastCallContext
|
||||
|
||||
self.screencastJoinDisposable.set((screencastCallContext.joinPayload
|
||||
@ -3013,7 +3032,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
|
||||
strongSelf.requestDisposable.set((strongSelf.accountContext.engine.calls.joinGroupCallAsScreencast(
|
||||
peerId: strongSelf.peerId,
|
||||
callId: callInfo.id,
|
||||
accessHash: callInfo.accessHash,
|
||||
joinPayload: joinPayload.0
|
||||
@ -3374,6 +3392,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
private func restartMyAudioLevelTimer() {
|
||||
self.myAudioLevelTimer?.invalidate()
|
||||
|
||||
guard let peerId = self.peerId else {
|
||||
return
|
||||
}
|
||||
let myAudioLevelTimer = SwiftSignalKit.Timer(timeout: 0.1, repeat: false, completion: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -3395,7 +3417,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
if shouldBeSendingTyping != strongSelf.isSendingTyping {
|
||||
strongSelf.isSendingTyping = shouldBeSendingTyping
|
||||
if shouldBeSendingTyping {
|
||||
strongSelf.typingDisposable.set(strongSelf.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: strongSelf.peerId, category: .voiceChat), activity: .speakingInGroupCall(timestamp: 0)))
|
||||
strongSelf.typingDisposable.set(strongSelf.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: peerId, category: .voiceChat), activity: .speakingInGroupCall(timestamp: 0)))
|
||||
strongSelf.restartMyAudioLevelTimer()
|
||||
} else {
|
||||
strongSelf.typingDisposable.set(nil)
|
||||
@ -3422,146 +3444,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.participantsContext?.updateDefaultParticipantsAreMuted(isMuted: isMuted)
|
||||
}
|
||||
|
||||
public func makeIncomingVideoView(endpointId: String, requestClone: Bool, completion: @escaping (PresentationCallVideoView?, PresentationCallVideoView?) -> Void) {
|
||||
if endpointId == self.currentLocalEndpointId {
|
||||
self.makeOutgoingVideoView(requestClone: requestClone, completion: completion)
|
||||
return
|
||||
}
|
||||
|
||||
self.genericCallContext?.makeIncomingVideoView(endpointId: endpointId, requestClone: requestClone, completion: { mainView, cloneView in
|
||||
if let mainView = mainView {
|
||||
let setOnFirstFrameReceived = mainView.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = mainView.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = mainView.setOnIsMirroredUpdated
|
||||
let updateIsEnabled = mainView.updateIsEnabled
|
||||
let mainVideoView = PresentationCallVideoView(
|
||||
holder: mainView,
|
||||
view: mainView.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak mainView] in
|
||||
if let mainView = mainView {
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch mainView.getOrientation() {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
return mappedValue
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak mainView] in
|
||||
if let mainView = mainView {
|
||||
return mainView.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { value in
|
||||
updateIsEnabled(value)
|
||||
}
|
||||
)
|
||||
|
||||
var cloneVideoView: PresentationCallVideoView?
|
||||
if let cloneView = cloneView {
|
||||
let setOnFirstFrameReceived = cloneView.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = cloneView.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = cloneView.setOnIsMirroredUpdated
|
||||
let updateIsEnabled = cloneView.updateIsEnabled
|
||||
cloneVideoView = PresentationCallVideoView(
|
||||
holder: cloneView,
|
||||
view: cloneView.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak cloneView] in
|
||||
if let cloneView = cloneView {
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch cloneView.getOrientation() {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
return mappedValue
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak cloneView] in
|
||||
if let cloneView = cloneView {
|
||||
return cloneView.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { value in
|
||||
updateIsEnabled(value)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
completion(mainVideoView, cloneVideoView)
|
||||
} else {
|
||||
completion(nil, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func video(endpointId: String) -> Signal<OngoingGroupCallContext.VideoFrameData, NoError>? {
|
||||
return Signal { [weak self] subscriber in
|
||||
guard let self else {
|
||||
|
@ -327,8 +327,11 @@ final class VideoChatScreenComponent: Component {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let peerId = component.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.call.accountContext.account.postbox.loadedPeerWithId(component.call.peerId)
|
||||
let _ = (component.call.accountContext.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] chatPeer in
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
@ -392,6 +395,9 @@ final class VideoChatScreenComponent: Component {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let peerId = component.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let formatSendTitle: (String) -> String = { string in
|
||||
var string = string
|
||||
@ -405,7 +411,7 @@ final class VideoChatScreenComponent: Component {
|
||||
return string
|
||||
}
|
||||
|
||||
let _ = (component.call.accountContext.account.postbox.loadedPeerWithId(component.call.peerId)
|
||||
let _ = (component.call.accountContext.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
@ -975,9 +981,15 @@ final class VideoChatScreenComponent: Component {
|
||||
|> map { peer in
|
||||
return [FoundPeer(peer: peer, subscribers: nil)]
|
||||
}
|
||||
let cachedDisplayAsAvailablePeers: Signal<[FoundPeer], NoError>
|
||||
if let peerId = component.call.peerId {
|
||||
cachedDisplayAsAvailablePeers = component.call.accountContext.engine.calls.cachedGroupCallDisplayAsAvailablePeers(peerId: peerId)
|
||||
} else {
|
||||
cachedDisplayAsAvailablePeers = .single([])
|
||||
}
|
||||
let displayAsPeers: Signal<[FoundPeer], NoError> = currentAccountPeer
|
||||
|> then(
|
||||
combineLatest(currentAccountPeer, component.call.accountContext.engine.calls.cachedGroupCallDisplayAsAvailablePeers(peerId: component.call.peerId))
|
||||
combineLatest(currentAccountPeer, cachedDisplayAsAvailablePeers)
|
||||
|> map { currentAccountPeer, availablePeers -> [FoundPeer] in
|
||||
var result = currentAccountPeer
|
||||
result.append(contentsOf: availablePeers)
|
||||
@ -2016,10 +2028,16 @@ final class VideoChatScreenV2Impl: ViewControllerComponentContainer, VoiceChatCo
|
||||
}
|
||||
|
||||
static func initialData(call: PresentationGroupCall) -> Signal<InitialData, NoError> {
|
||||
let callPeer: Signal<EnginePeer?, NoError>
|
||||
if let peerId = call.peerId {
|
||||
callPeer = call.accountContext.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||
)
|
||||
} else {
|
||||
callPeer = .single(nil)
|
||||
}
|
||||
return combineLatest(
|
||||
call.accountContext.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: call.peerId)
|
||||
),
|
||||
callPeer,
|
||||
call.members |> take(1),
|
||||
call.state |> take(1)
|
||||
)
|
||||
|
@ -38,12 +38,15 @@ extension VideoChatScreenComponent.View {
|
||||
guard let inviteType else {
|
||||
return
|
||||
}
|
||||
guard let peerId = component.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
switch inviteType {
|
||||
case .invite:
|
||||
let groupPeer = component.call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: component.call.peerId))
|
||||
let groupPeer = component.call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
let _ = (groupPeer
|
||||
|> deliverOnMainQueue).start(next: { [weak self] groupPeer in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] groupPeer in
|
||||
guard let self, let component = self.component, let environment = self.environment, let groupPeer else {
|
||||
return
|
||||
}
|
||||
@ -299,13 +302,15 @@ extension VideoChatScreenComponent.View {
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
let callPeerId = component.call.peerId
|
||||
guard let callPeerId = component.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.call.accountContext.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: callPeerId),
|
||||
TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: callPeerId)
|
||||
)
|
||||
|> map { peer, exportedInvitation -> String? in
|
||||
|> map { peer, exportedInvitation -> String? in
|
||||
if let link = inviteLinks?.listenerLink {
|
||||
return link
|
||||
} else if let peer = peer, let addressName = peer.addressName, !addressName.isEmpty {
|
||||
|
@ -268,8 +268,11 @@ extension VideoChatScreenComponent.View {
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let peerId = component.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.call.accountContext.account.postbox.loadedPeerWithId(component.call.peerId)
|
||||
let _ = (component.call.accountContext.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] chatPeer in
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
@ -288,8 +291,11 @@ extension VideoChatScreenComponent.View {
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
}
|
||||
guard let callPeerId = component.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = component.call.accountContext.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: component.call.accountContext.engine, peerId: component.call.peerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)).start()
|
||||
let _ = component.call.accountContext.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: component.call.accountContext.engine, peerId: callPeerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)).start()
|
||||
component.call.removedPeer(peer.id)
|
||||
|
||||
self.presentUndoOverlay(content: .banned(text: environment.strings.VoiceChat_RemovedPeerText(EnginePeer(peer).displayTitle(strings: environment.strings, displayOrder: nameDisplayOrder)).string), action: { _ in return false })
|
||||
|
@ -1164,15 +1164,17 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
strongSelf.isNoiseSuppressionEnabled = value
|
||||
})
|
||||
|
||||
let displayAsPeers: Signal<[FoundPeer], NoError> = currentAccountPeer
|
||||
|> then(
|
||||
combineLatest(currentAccountPeer, context.engine.calls.cachedGroupCallDisplayAsAvailablePeers(peerId: call.peerId))
|
||||
|> map { currentAccountPeer, availablePeers -> [FoundPeer] in
|
||||
var result = currentAccountPeer
|
||||
result.append(contentsOf: availablePeers)
|
||||
return result
|
||||
}
|
||||
)
|
||||
var displayAsPeers: Signal<[FoundPeer], NoError> = currentAccountPeer
|
||||
if let callPeerId = call.peerId {
|
||||
displayAsPeers = displayAsPeers |> then(
|
||||
combineLatest(currentAccountPeer, context.engine.calls.cachedGroupCallDisplayAsAvailablePeers(peerId: callPeerId))
|
||||
|> map { currentAccountPeer, availablePeers -> [FoundPeer] in
|
||||
var result = currentAccountPeer
|
||||
result.append(contentsOf: availablePeers)
|
||||
return result
|
||||
}
|
||||
)
|
||||
}
|
||||
self.displayAsPeersPromise.set(displayAsPeers)
|
||||
|
||||
self.inviteLinksPromise.set(.single(nil)
|
||||
@ -1197,8 +1199,11 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let callPeerId = strongSelf.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let groupPeer = strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.call.peerId))
|
||||
let groupPeer = strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: callPeerId))
|
||||
let _ = combineLatest(queue: Queue.mainQueue(), groupPeer, strongSelf.inviteLinksPromise.get() |> take(1)).start(next: { groupPeer, inviteLinks in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -1446,7 +1451,9 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let callPeerId = strongSelf.call.peerId
|
||||
guard let callPeerId = strongSelf.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: callPeerId),
|
||||
@ -1711,8 +1718,11 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let callPeerId = strongSelf.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.call.peerId)
|
||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(callPeerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] chatPeer in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -1730,7 +1740,7 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = strongSelf.context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: strongSelf.context.engine, peerId: strongSelf.call.peerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)).start()
|
||||
let _ = strongSelf.context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: strongSelf.context.engine, peerId: callPeerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)).start()
|
||||
strongSelf.call.removedPeer(peer.id)
|
||||
|
||||
strongSelf.presentUndoOverlay(content: .banned(text: strongSelf.presentationData.strings.VoiceChat_RemovedPeerText(EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string), action: { _ in return false })
|
||||
@ -1990,17 +2000,24 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
}
|
||||
})
|
||||
|
||||
let callPeerView: Signal<PeerView?, NoError>
|
||||
if let peerId = self.call.peerId {
|
||||
callPeerView = self.context.account.viewTracker.peerView(peerId) |> map(Optional.init)
|
||||
} else {
|
||||
callPeerView = .single(nil)
|
||||
}
|
||||
|
||||
let titleAndRecording: Signal<(String?, Bool), NoError> = self.call.state
|
||||
|> map { state -> (String?, Bool) in
|
||||
return (state.title, state.recordingStartTimestamp != nil)
|
||||
}
|
||||
self.peerViewDisposable = combineLatest(queue: Queue.mainQueue(), self.context.account.viewTracker.peerView(self.call.peerId), titleAndRecording).start(next: { [weak self] view, titleAndRecording in
|
||||
self.peerViewDisposable = combineLatest(queue: Queue.mainQueue(), callPeerView, titleAndRecording).start(next: { [weak self] view, titleAndRecording in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let (title, isRecording) = titleAndRecording
|
||||
if let peer = peerViewMainPeer(view) {
|
||||
if let view, let peer = peerViewMainPeer(view) {
|
||||
let isLivestream: Bool
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
isLivestream = true
|
||||
@ -2509,10 +2526,13 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
guard let myPeerId = self.callState?.myPeerId else {
|
||||
return .single([])
|
||||
}
|
||||
guard let callPeerId = self.call.peerId else {
|
||||
return .single([])
|
||||
}
|
||||
|
||||
let canManageCall = self.callState?.canManageCall == true
|
||||
let avatarSize = CGSize(width: 28.0, height: 28.0)
|
||||
return combineLatest(self.displayAsPeersPromise.get(), self.context.account.postbox.loadedPeerWithId(self.call.peerId), self.inviteLinksPromise.get())
|
||||
return combineLatest(self.displayAsPeersPromise.get(), self.context.account.postbox.loadedPeerWithId(callPeerId), self.inviteLinksPromise.get())
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> map { [weak self] peers, chatPeer, inviteLinks -> [ContextMenuItem] in
|
||||
@ -3398,7 +3418,11 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
return string
|
||||
}
|
||||
|
||||
let _ = (self.context.account.postbox.loadedPeerWithId(self.call.peerId)
|
||||
guard let callPeerId = self.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (self.context.account.postbox.loadedPeerWithId(callPeerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
var inviteLinks = inviteLinks
|
||||
@ -3630,10 +3654,13 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let callPeerId = strongSelf.call.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.call.peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: strongSelf.call.peerId)
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: callPeerId),
|
||||
TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: callPeerId)
|
||||
)
|
||||
|> map { peer, exportedInvitation -> GroupCallInviteLinks? in
|
||||
if let inviteLinks = inviteLinks {
|
||||
@ -6072,7 +6099,10 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
}
|
||||
|
||||
private func openTitleEditing() {
|
||||
let _ = (self.context.account.postbox.loadedPeerWithId(self.call.peerId)
|
||||
guard let callPeerId = self.call.peerId else {
|
||||
return
|
||||
}
|
||||
let _ = (self.context.account.postbox.loadedPeerWithId(callPeerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] chatPeer in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -115,7 +115,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdateChatListFilter(id: Int32, filter: Api.DialogFilter?)
|
||||
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?)
|
||||
case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32)
|
||||
case UpdateGroupCall(peerId: PeerId, call: Api.GroupCall)
|
||||
case UpdateGroupCall(peerId: PeerId?, call: Api.GroupCall)
|
||||
case UpdateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?)
|
||||
case UpdateAttachMenuBots
|
||||
case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String)
|
||||
@ -398,7 +398,7 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version))
|
||||
}
|
||||
|
||||
mutating func updateGroupCall(peerId: PeerId, call: Api.GroupCall) {
|
||||
mutating func updateGroupCall(peerId: PeerId?, call: Api.GroupCall) {
|
||||
self.addOperation(.UpdateGroupCall(peerId: peerId, call: call))
|
||||
}
|
||||
|
||||
|
@ -1616,9 +1616,9 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
||||
case let .inputGroupCall(id, accessHash):
|
||||
updatedState.updateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version)
|
||||
}
|
||||
case let .updateGroupCall(channelId, call):
|
||||
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), call: call)
|
||||
updatedState.updateGroupCall(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(channelId)), call: call)
|
||||
case let .updateGroupCall(_, channelId, call):
|
||||
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
||||
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
||||
case let .updatePeerHistoryTTL(_, peer, ttl):
|
||||
updatedState.updateAutoremoveTimeout(peer: peer, value: CachedPeerAutoremoveTimeout.Value(ttl))
|
||||
case let .updateLangPackTooLong(langCode):
|
||||
@ -4451,18 +4451,20 @@ func replayFinalState(
|
||||
switch call {
|
||||
case .groupCall:
|
||||
if let info = GroupCallInfo(call) {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedChannelData {
|
||||
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
|
||||
} else if let current = current as? CachedGroupData {
|
||||
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
if let peerId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedChannelData {
|
||||
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
|
||||
} else if let current = current as? CachedGroupData {
|
||||
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
switch call {
|
||||
case let .groupCall(flags, _, _, participantsCount, title, _, recordStartDate, scheduleDate, _, _, _):
|
||||
case let .groupCall(flags, _, _, participantsCount, title, _, recordStartDate, scheduleDate, _, _, _, _):
|
||||
let isMuted = (flags & (1 << 1)) != 0
|
||||
let canChange = (flags & (1 << 2)) != 0
|
||||
let isVideoEnabled = (flags & (1 << 9)) != 0
|
||||
@ -4481,23 +4483,25 @@ func replayFinalState(
|
||||
.call(isTerminated: true, defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false), title: nil, recordingStartTimestamp: nil, scheduleTimestamp: nil, isVideoEnabled: false, participantCount: nil)
|
||||
))
|
||||
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedChannelData {
|
||||
if let activeCall = current.activeCall, activeCall.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
if let peerId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedChannelData {
|
||||
if let activeCall = current.activeCall, activeCall.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
} else if let current = current as? CachedGroupData {
|
||||
if let activeCall = current.activeCall, activeCall.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
} else if let current = current as? CachedGroupData {
|
||||
if let activeCall = current.activeCall, activeCall.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
case let .UpdateAutoremoveTimeout(peer, autoremoveValue):
|
||||
let peerId = peer.peerId
|
||||
|
@ -53,6 +53,25 @@ public struct CallId: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public struct GroupCallReference: Equatable {
|
||||
public var id: Int64
|
||||
public var accessHash: Int64
|
||||
|
||||
public init(id: Int64, accessHash: Int64) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
}
|
||||
}
|
||||
|
||||
extension GroupCallReference {
|
||||
init(_ apiGroupCall: Api.InputGroupCall) {
|
||||
switch apiGroupCall {
|
||||
case let .inputGroupCall(id, accessHash):
|
||||
self.init(id: id, accessHash: accessHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CallSessionInternalState {
|
||||
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String])
|
||||
case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, disposable: Disposable)
|
||||
@ -60,7 +79,7 @@ enum CallSessionInternalState {
|
||||
case requesting(a: Data, disposable: Disposable)
|
||||
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?)
|
||||
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable)
|
||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
|
||||
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
|
||||
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
||||
|
||||
@ -78,7 +97,7 @@ enum CallSessionInternalState {
|
||||
return id
|
||||
case let .confirming(id, _, _, _, _, _):
|
||||
return id
|
||||
case let .active(id, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return id
|
||||
case .dropping:
|
||||
return nil
|
||||
@ -143,7 +162,7 @@ public enum CallSessionState {
|
||||
case ringing
|
||||
case accepting
|
||||
case requesting(ringing: Bool)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
|
||||
case dropping(reason: CallSessionTerminationReason)
|
||||
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
||||
|
||||
@ -159,8 +178,8 @@ public enum CallSessionState {
|
||||
self = .requesting(ringing: true)
|
||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall)
|
||||
case let .dropping(reason, _):
|
||||
self = .dropping(reason: reason)
|
||||
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
||||
@ -316,7 +335,7 @@ private final class CallSessionContext {
|
||||
var signalingReceiver: (([Data]) -> Void)?
|
||||
|
||||
let signalingDisposables = DisposableSet()
|
||||
|
||||
var createConferenceCallDisposable: Disposable?
|
||||
let acknowledgeIncomingCallDisposable = MetaDisposable()
|
||||
|
||||
var isEmpty: Bool {
|
||||
@ -336,7 +355,9 @@ private final class CallSessionContext {
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.signalingDisposables.dispose()
|
||||
self.acknowledgeIncomingCallDisposable.dispose()
|
||||
self.createConferenceCallDisposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
@ -615,7 +636,7 @@ private final class CallSessionManagerContext {
|
||||
case let .accepting(id, accessHash, _, _, disposable):
|
||||
dropData = (id, accessHash, .abort)
|
||||
disposable.dispose()
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _):
|
||||
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
||||
let internalReason: DropCallSessionReason
|
||||
switch reason {
|
||||
@ -731,9 +752,9 @@ private final class CallSessionManagerContext {
|
||||
case let .waiting(config):
|
||||
context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P):
|
||||
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
|
||||
if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) {
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@ -754,7 +775,7 @@ private final class CallSessionManagerContext {
|
||||
func sendSignalingData(internalId: CallSessionInternalId, data: Data) {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||
context.signalingDisposables.add(self.network.request(Api.functions.phone.sendSignalingData(peer: .inputPhoneCall(id: id, accessHash: accessHash), data: Buffer(data: data))).start())
|
||||
default:
|
||||
break
|
||||
@ -762,6 +783,42 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
|
||||
func createConferenceIfNecessary(internalId: CallSessionInternalId) {
|
||||
if let context = self.contexts[internalId] {
|
||||
if context.createConferenceCallDisposable != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall):
|
||||
if conferenceCall != nil {
|
||||
return
|
||||
}
|
||||
|
||||
context.createConferenceCallDisposable = (createConferenceCall(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, callId: CallId(id: id, accessHash: accessHash))
|
||||
|> deliverOn(self.queue)).startStrict(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let context = self.contexts[internalId] else {
|
||||
return
|
||||
}
|
||||
if let result {
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, _):
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: result)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateCallType(internalId: CallSessionInternalId, type: CallSession.CallType) {
|
||||
if let context = self.contexts[internalId] {
|
||||
context.type = type
|
||||
@ -856,7 +913,7 @@ private final class CallSessionManagerContext {
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||
@ -884,7 +941,7 @@ private final class CallSessionManagerContext {
|
||||
//assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters):
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters, conferenceCall):
|
||||
let allowsP2P = (flags & (1 << 5)) != 0
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
if let context = self.contexts[internalId] {
|
||||
@ -908,7 +965,7 @@ private final class CallSessionManagerContext {
|
||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||
context.isVideoPossible = isVideoPossible
|
||||
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@ -935,7 +992,7 @@ private final class CallSessionManagerContext {
|
||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||
context.isVideoPossible = isVideoPossible
|
||||
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@ -1166,6 +1223,12 @@ public final class CallSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
public func createConferenceIfNecessary(internalId: CallSessionInternalId) {
|
||||
self.withContext { context in
|
||||
context.createConferenceIfNecessary(internalId: internalId)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateCallType(internalId: CallSessionInternalId, type: CallSession.CallType) {
|
||||
self.withContext { context in
|
||||
context.updateCallType(internalId: internalId, type: type)
|
||||
@ -1215,7 +1278,7 @@ public final class CallSessionManager {
|
||||
|
||||
private enum AcceptedCall {
|
||||
case waiting(config: SecretChatEncryptionConfig)
|
||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
|
||||
}
|
||||
|
||||
private enum AcceptCallResult {
|
||||
@ -1255,7 +1318,7 @@ private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network:
|
||||
return .failed
|
||||
case .phoneCallWaiting:
|
||||
return .success(.waiting(config: config))
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate, customParameters):
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate, customParameters, conferenceCall):
|
||||
if id == stableId {
|
||||
switch callProtocol{
|
||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||
@ -1268,7 +1331,7 @@ private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network:
|
||||
customParametersValue = data
|
||||
}
|
||||
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0))
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0, conferenceCall: conferenceCall.flatMap(GroupCallReference.init)))
|
||||
} else {
|
||||
return .failed
|
||||
}
|
||||
@ -1432,3 +1495,61 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
||||
return .single((reportRating, sendDebugLogs))
|
||||
}
|
||||
}
|
||||
|
||||
private func createConferenceCall(postbox: Postbox, network: Network, accountPeerId: PeerId, callId: CallId) -> Signal<GroupCallReference?, NoError> {
|
||||
#if DEBUG && false
|
||||
if "".isEmpty {
|
||||
return _internal_resolvePeerByName(postbox: postbox, network: network, accountPeerId: accountPeerId, name: "qwfqwfqwefqwef22", referrer: nil)
|
||||
|> mapToSignal { result -> Signal<PeerId?, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .never()
|
||||
case let .result(peerId):
|
||||
return .single(peerId)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { peerId -> Signal<GroupCallReference?, NoError> in
|
||||
guard let peerId else {
|
||||
return .single(nil)
|
||||
}
|
||||
return _internal_updatedCurrentPeerGroupCall(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId)
|
||||
|> map { result -> GroupCallReference? in
|
||||
guard let result else {
|
||||
return nil
|
||||
}
|
||||
return GroupCallReference(id: result.id, accessHash: result.accessHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return network.request(Api.functions.phone.createConferenceCall(peer: .inputPhoneCall(id: callId.id, accessHash: callId.accessHash)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<GroupCallReference?, NoError> in
|
||||
guard let result else {
|
||||
return .single(nil)
|
||||
}
|
||||
return postbox.transaction { transaction -> GroupCallReference? in
|
||||
switch result {
|
||||
case let .phoneCall(phoneCall, users):
|
||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users))
|
||||
|
||||
switch phoneCall {
|
||||
case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded:
|
||||
return nil
|
||||
case .phoneCallWaiting:
|
||||
return nil
|
||||
case let .phoneCall(_, _, _, _, _, _, _, _, _, _, _, _, conferenceCall):
|
||||
if let conferenceCall {
|
||||
return GroupCallReference(conferenceCall)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ public struct GroupCallInfo: Equatable {
|
||||
public var isVideoEnabled: Bool
|
||||
public var unmutedVideoLimit: Int
|
||||
public var isStream: Bool
|
||||
public var upgradedPrivateCallId: Int64?
|
||||
|
||||
public init(
|
||||
id: Int64,
|
||||
@ -31,7 +32,8 @@ public struct GroupCallInfo: Equatable {
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?,
|
||||
isVideoEnabled: Bool,
|
||||
unmutedVideoLimit: Int,
|
||||
isStream: Bool
|
||||
isStream: Bool,
|
||||
upgradedPrivateCallId: Int64?
|
||||
) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
@ -46,6 +48,7 @@ public struct GroupCallInfo: Equatable {
|
||||
self.isVideoEnabled = isVideoEnabled
|
||||
self.unmutedVideoLimit = unmutedVideoLimit
|
||||
self.isStream = isStream
|
||||
self.upgradedPrivateCallId = upgradedPrivateCallId
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +60,7 @@ public struct GroupCallSummary: Equatable {
|
||||
extension GroupCallInfo {
|
||||
init?(_ call: Api.GroupCall) {
|
||||
switch call {
|
||||
case let .groupCall(flags, id, accessHash, participantsCount, title, streamDcId, recordStartDate, scheduleDate, _, unmutedVideoLimit, _):
|
||||
case let .groupCall(flags, id, accessHash, participantsCount, title, streamDcId, recordStartDate, scheduleDate, _, unmutedVideoLimit, _, conferenceFromCall):
|
||||
self.init(
|
||||
id: id,
|
||||
accessHash: accessHash,
|
||||
@ -71,7 +74,8 @@ extension GroupCallInfo {
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0),
|
||||
isVideoEnabled: (flags & (1 << 9)) != 0,
|
||||
unmutedVideoLimit: Int(unmutedVideoLimit),
|
||||
isStream: (flags & (1 << 12)) != 0
|
||||
isStream: (flags & (1 << 12)) != 0,
|
||||
upgradedPrivateCallId: conferenceFromCall
|
||||
)
|
||||
case .groupCallDiscarded:
|
||||
return nil
|
||||
@ -163,7 +167,7 @@ func _internal_createGroupCall(account: Account, peerId: PeerId, title: String?,
|
||||
var parsedCall: GroupCallInfo?
|
||||
loop: for update in result.allUpdates {
|
||||
switch update {
|
||||
case let .updateGroupCall(_, call):
|
||||
case let .updateGroupCall(_, _, call):
|
||||
parsedCall = GroupCallInfo(call)
|
||||
break loop
|
||||
default:
|
||||
@ -208,7 +212,7 @@ func _internal_startScheduledGroupCall(account: Account, peerId: PeerId, callId:
|
||||
var parsedCall: GroupCallInfo?
|
||||
loop: for update in result.allUpdates {
|
||||
switch update {
|
||||
case let .updateGroupCall(_, call):
|
||||
case let .updateGroupCall(_, _, call):
|
||||
parsedCall = GroupCallInfo(call)
|
||||
break loop
|
||||
default:
|
||||
@ -265,7 +269,7 @@ func _internal_toggleScheduledGroupCallSubscription(account: Account, peerId: Pe
|
||||
var parsedCall: GroupCallInfo?
|
||||
loop: for update in result.allUpdates {
|
||||
switch update {
|
||||
case let .updateGroupCall(_, call):
|
||||
case let .updateGroupCall(_, _, call):
|
||||
parsedCall = GroupCallInfo(call)
|
||||
break loop
|
||||
default:
|
||||
@ -341,17 +345,17 @@ public enum GetGroupCallParticipantsError {
|
||||
func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
||||
let accountPeerId = account.peerId
|
||||
|
||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError>
|
||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Int64?), GetGroupCallParticipantsError>
|
||||
|
||||
sortAscendingValue = _internal_getCurrentGroupCall(account: account, callId: callId, accessHash: accessHash)
|
||||
|> mapError { _ -> GetGroupCallParticipantsError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError> in
|
||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Int64?), GetGroupCallParticipantsError> in
|
||||
guard let result = result else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit, result.info.isStream))
|
||||
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit, result.info.isStream, result.info.upgradedPrivateCallId))
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
@ -368,7 +372,7 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH
|
||||
let version: Int32
|
||||
let nextParticipantsFetchOffset: String?
|
||||
|
||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream) = sortAscendingAndScheduleTimestamp
|
||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream, upgradedPrivateCallId) = sortAscendingAndScheduleTimestamp
|
||||
|
||||
switch result {
|
||||
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
|
||||
@ -404,6 +408,7 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH
|
||||
isVideoEnabled: isVideoEnabled,
|
||||
unmutedVideoLimit: unmutedVideoLimit,
|
||||
isStream: isStream,
|
||||
upgradedPrivateCallId: upgradedPrivateCallId,
|
||||
version: version
|
||||
)
|
||||
}
|
||||
@ -430,7 +435,7 @@ public struct JoinGroupCallResult {
|
||||
public var jsonParams: String
|
||||
}
|
||||
|
||||
func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||
func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
if let joinAs = joinAs {
|
||||
return transaction.getPeer(joinAs).flatMap(apiInputPeer)
|
||||
@ -460,33 +465,37 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
|
||||
} else if error.errorDescription == "GROUPCALL_PARTICIPANTS_TOO_MUCH" {
|
||||
return .fail(.tooManyParticipants)
|
||||
} else if error.errorDescription == "JOIN_AS_PEER_INVALID" {
|
||||
let _ = (account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedChannelData {
|
||||
return current.withUpdatedCallJoinPeerId(nil)
|
||||
} else if let current = current as? CachedGroupData {
|
||||
return current.withUpdatedCallJoinPeerId(nil)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
}).start()
|
||||
if let peerId {
|
||||
let _ = (account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedChannelData {
|
||||
return current.withUpdatedCallJoinPeerId(nil)
|
||||
} else if let current = current as? CachedGroupData {
|
||||
return current.withUpdatedCallJoinPeerId(nil)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
}).start()
|
||||
}
|
||||
|
||||
return .fail(.invalidJoinAsPeer)
|
||||
} else if error.errorDescription == "GROUPCALL_INVALID" {
|
||||
return account.postbox.transaction { transaction -> Signal<Api.Updates, JoinGroupCallError> in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedGroupData {
|
||||
if current.activeCall?.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
if let peerId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
if let current = current as? CachedGroupData {
|
||||
if current.activeCall?.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
}
|
||||
} else if let current = current as? CachedChannelData {
|
||||
if current.activeCall?.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
}
|
||||
}
|
||||
} else if let current = current as? CachedChannelData {
|
||||
if current.activeCall?.id == callId {
|
||||
return current.withUpdatedActiveCall(nil)
|
||||
}
|
||||
}
|
||||
return current
|
||||
})
|
||||
return current
|
||||
})
|
||||
}
|
||||
|
||||
return .fail(.generic)
|
||||
}
|
||||
@ -508,7 +517,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
|
||||
)
|
||||
|> mapToSignal { updates, participantsState -> Signal<JoinGroupCallResult, JoinGroupCallError> in
|
||||
let peer = account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(peerId)
|
||||
return peerId.flatMap(transaction.getPeer)
|
||||
}
|
||||
|> castError(JoinGroupCallError.self)
|
||||
|
||||
@ -517,18 +526,16 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
|
||||
peer
|
||||
)
|
||||
|> mapToSignal { peerAdminIds, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in
|
||||
guard let peer = peer else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
var state = participantsState
|
||||
if let channel = peer as? TelegramChannel {
|
||||
state.isCreator = channel.flags.contains(.isCreator)
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
if case .creator = group.role {
|
||||
state.isCreator = true
|
||||
} else {
|
||||
state.isCreator = false
|
||||
if let peer {
|
||||
if let channel = peer as? TelegramChannel {
|
||||
state.isCreator = channel.flags.contains(.isCreator)
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
if case .creator = group.role {
|
||||
state.isCreator = true
|
||||
} else {
|
||||
state.isCreator = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -538,11 +545,11 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
|
||||
var maybeParsedClientParams: String?
|
||||
loop: for update in updates.allUpdates {
|
||||
switch update {
|
||||
case let .updateGroupCall(_, call):
|
||||
case let .updateGroupCall(_, _, call):
|
||||
maybeParsedCall = GroupCallInfo(call)
|
||||
|
||||
switch call {
|
||||
case let .groupCall(flags, _, _, _, title, _, recordStartDate, scheduleDate, _, unmutedVideoLimit, _):
|
||||
case let .groupCall(flags, _, _, _, title, _, recordStartDate, scheduleDate, _, unmutedVideoLimit, _, _):
|
||||
let isMuted = (flags & (1 << 1)) != 0
|
||||
let canChange = (flags & (1 << 2)) != 0
|
||||
let isVideoEnabled = (flags & (1 << 9)) != 0
|
||||
@ -589,15 +596,17 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> JoinGroupCallResult in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: parsedCall.isStream))
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: parsedCall.isStream))
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
if let peerId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: parsedCall.isStream))
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: parsedCall.isStream))
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var state = state
|
||||
|
||||
@ -672,7 +681,7 @@ public struct JoinGroupCallAsScreencastResult {
|
||||
public var endpointId: String
|
||||
}
|
||||
|
||||
func _internal_joinGroupCallAsScreencast(account: Account, peerId: PeerId, callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
||||
func _internal_joinGroupCallAsScreencast(account: Account, callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
||||
return account.network.request(Api.functions.phone.joinGroupCallPresentation(call: .inputGroupCall(id: callId, accessHash: accessHash), params: .dataJSON(data: joinPayload)))
|
||||
|> mapError { _ -> JoinGroupCallError in
|
||||
return .generic
|
||||
@ -752,39 +761,41 @@ public enum StopGroupCallError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func _internal_stopGroupCall(account: Account, peerId: PeerId, callId: Int64, accessHash: Int64) -> Signal<Never, StopGroupCallError> {
|
||||
func _internal_stopGroupCall(account: Account, peerId: PeerId?, callId: Int64, accessHash: Int64) -> Signal<Never, StopGroupCallError> {
|
||||
return account.network.request(Api.functions.phone.discardGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|
||||
|> mapError { _ -> StopGroupCallError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Never, StopGroupCallError> in
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
return cachedData.withUpdatedActiveCall(nil).withUpdatedCallJoinPeerId(nil)
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
return cachedData.withUpdatedActiveCall(nil).withUpdatedCallJoinPeerId(nil)
|
||||
} else {
|
||||
return cachedData
|
||||
if let peerId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
return cachedData.withUpdatedActiveCall(nil).withUpdatedCallJoinPeerId(nil)
|
||||
} else if let cachedData = cachedData as? CachedGroupData {
|
||||
return cachedData.withUpdatedActiveCall(nil).withUpdatedCallJoinPeerId(nil)
|
||||
} else {
|
||||
return cachedData
|
||||
}
|
||||
})
|
||||
if var peer = transaction.getPeer(peerId) as? TelegramChannel {
|
||||
var flags = peer.flags
|
||||
flags.remove(.hasVoiceChat)
|
||||
flags.remove(.hasActiveVoiceChat)
|
||||
peer = peer.withUpdatedFlags(flags)
|
||||
updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
if var peer = transaction.getPeer(peerId) as? TelegramGroup {
|
||||
var flags = peer.flags
|
||||
flags.remove(.hasVoiceChat)
|
||||
flags.remove(.hasActiveVoiceChat)
|
||||
peer = peer.updateFlags(flags: flags, version: peer.version)
|
||||
updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
})
|
||||
if var peer = transaction.getPeer(peerId) as? TelegramChannel {
|
||||
var flags = peer.flags
|
||||
flags.remove(.hasVoiceChat)
|
||||
flags.remove(.hasActiveVoiceChat)
|
||||
peer = peer.withUpdatedFlags(flags)
|
||||
updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
if var peer = transaction.getPeer(peerId) as? TelegramGroup {
|
||||
var flags = peer.flags
|
||||
flags.remove(.hasVoiceChat)
|
||||
flags.remove(.hasActiveVoiceChat)
|
||||
peer = peer.updateFlags(flags: flags, version: peer.version)
|
||||
updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
|
||||
account.stateManager.addUpdates(result)
|
||||
@ -1023,6 +1034,7 @@ public final class GroupCallParticipantsContext {
|
||||
public var isVideoEnabled: Bool
|
||||
public var unmutedVideoLimit: Int
|
||||
public var isStream: Bool
|
||||
public var upgradedPrivateCallId: Int64?
|
||||
public var version: Int32
|
||||
|
||||
public mutating func mergeActivity(from other: State, myPeerId: PeerId?, previousMyPeerId: PeerId?, mergeActivityTimestamps: Bool) {
|
||||
@ -1058,6 +1070,7 @@ public final class GroupCallParticipantsContext {
|
||||
isVideoEnabled: Bool,
|
||||
unmutedVideoLimit: Int,
|
||||
isStream: Bool,
|
||||
upgradedPrivateCallId: Int64?,
|
||||
version: Int32
|
||||
) {
|
||||
self.participants = participants
|
||||
@ -1074,6 +1087,7 @@ public final class GroupCallParticipantsContext {
|
||||
self.isVideoEnabled = isVideoEnabled
|
||||
self.unmutedVideoLimit = unmutedVideoLimit
|
||||
self.isStream = isStream
|
||||
self.upgradedPrivateCallId = upgradedPrivateCallId
|
||||
self.version = version
|
||||
}
|
||||
}
|
||||
@ -1190,7 +1204,7 @@ public final class GroupCallParticipantsContext {
|
||||
}
|
||||
|
||||
private let account: Account
|
||||
private let peerId: PeerId
|
||||
private let peerId: PeerId?
|
||||
public let myPeerId: PeerId
|
||||
public let id: Int64
|
||||
public let accessHash: Int64
|
||||
@ -1294,7 +1308,7 @@ public final class GroupCallParticipantsContext {
|
||||
|
||||
public private(set) var serviceState: ServiceState
|
||||
|
||||
init(account: Account, peerId: PeerId, myPeerId: PeerId, id: Int64, accessHash: Int64, state: State, previousServiceState: ServiceState?) {
|
||||
init(account: Account, peerId: PeerId?, myPeerId: PeerId, id: Int64, accessHash: Int64, state: State, previousServiceState: ServiceState?) {
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.myPeerId = myPeerId
|
||||
@ -1320,70 +1334,73 @@ public final class GroupCallParticipantsContext {
|
||||
}
|
||||
}))
|
||||
|
||||
let activityCategory: PeerActivitySpace.Category = .voiceChat
|
||||
self.activitiesDisposable = (self.account.peerInputActivities(peerId: PeerActivitySpace(peerId: peerId, category: activityCategory))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] activities in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let peerIds = Set(activities.map { item -> PeerId in
|
||||
item.0
|
||||
})
|
||||
strongSelf.activeSpeakersValue = peerIds
|
||||
|
||||
if !strongSelf.hasReceivedSpeakingParticipantsReport {
|
||||
var updatedParticipants = strongSelf.stateValue.state.participants
|
||||
var indexMap: [PeerId: Int] = [:]
|
||||
for i in 0 ..< updatedParticipants.count {
|
||||
indexMap[updatedParticipants[i].peer.id] = i
|
||||
if let peerId {
|
||||
let activityCategory: PeerActivitySpace.Category = .voiceChat
|
||||
self.activitiesDisposable = (self.account.peerInputActivities(peerId: PeerActivitySpace(peerId: peerId, category: activityCategory))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] activities in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var updated = false
|
||||
|
||||
for (activityPeerId, activity) in activities {
|
||||
if case let .speakingInGroupCall(intTimestamp) = activity {
|
||||
let timestamp = Double(intTimestamp)
|
||||
|
||||
if let index = indexMap[activityPeerId] {
|
||||
if let activityTimestamp = updatedParticipants[index].activityTimestamp {
|
||||
if activityTimestamp < timestamp {
|
||||
let peerIds = Set(activities.map { item -> PeerId in
|
||||
item.0
|
||||
})
|
||||
strongSelf.activeSpeakersValue = peerIds
|
||||
|
||||
if !strongSelf.hasReceivedSpeakingParticipantsReport {
|
||||
var updatedParticipants = strongSelf.stateValue.state.participants
|
||||
var indexMap: [PeerId: Int] = [:]
|
||||
for i in 0 ..< updatedParticipants.count {
|
||||
indexMap[updatedParticipants[i].peer.id] = i
|
||||
}
|
||||
var updated = false
|
||||
|
||||
for (activityPeerId, activity) in activities {
|
||||
if case let .speakingInGroupCall(intTimestamp) = activity {
|
||||
let timestamp = Double(intTimestamp)
|
||||
|
||||
if let index = indexMap[activityPeerId] {
|
||||
if let activityTimestamp = updatedParticipants[index].activityTimestamp {
|
||||
if activityTimestamp < timestamp {
|
||||
updatedParticipants[index].activityTimestamp = timestamp
|
||||
updated = true
|
||||
}
|
||||
} else {
|
||||
updatedParticipants[index].activityTimestamp = timestamp
|
||||
updated = true
|
||||
}
|
||||
} else {
|
||||
updatedParticipants[index].activityTimestamp = timestamp
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if updated {
|
||||
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
||||
|
||||
strongSelf.stateValue = InternalState(
|
||||
state: State(
|
||||
participants: updatedParticipants,
|
||||
nextParticipantsFetchOffset: strongSelf.stateValue.state.nextParticipantsFetchOffset,
|
||||
adminIds: strongSelf.stateValue.state.adminIds,
|
||||
isCreator: strongSelf.stateValue.state.isCreator,
|
||||
defaultParticipantsAreMuted: strongSelf.stateValue.state.defaultParticipantsAreMuted,
|
||||
sortAscending: strongSelf.stateValue.state.sortAscending,
|
||||
recordingStartTimestamp: strongSelf.stateValue.state.recordingStartTimestamp,
|
||||
title: strongSelf.stateValue.state.title,
|
||||
scheduleTimestamp: strongSelf.stateValue.state.scheduleTimestamp,
|
||||
subscribedToScheduled: strongSelf.stateValue.state.subscribedToScheduled,
|
||||
totalCount: strongSelf.stateValue.state.totalCount,
|
||||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||
isStream: strongSelf.stateValue.state.isStream,
|
||||
version: strongSelf.stateValue.state.version
|
||||
),
|
||||
overlayState: strongSelf.stateValue.overlayState
|
||||
)
|
||||
if updated {
|
||||
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
||||
|
||||
strongSelf.stateValue = InternalState(
|
||||
state: State(
|
||||
participants: updatedParticipants,
|
||||
nextParticipantsFetchOffset: strongSelf.stateValue.state.nextParticipantsFetchOffset,
|
||||
adminIds: strongSelf.stateValue.state.adminIds,
|
||||
isCreator: strongSelf.stateValue.state.isCreator,
|
||||
defaultParticipantsAreMuted: strongSelf.stateValue.state.defaultParticipantsAreMuted,
|
||||
sortAscending: strongSelf.stateValue.state.sortAscending,
|
||||
recordingStartTimestamp: strongSelf.stateValue.state.recordingStartTimestamp,
|
||||
title: strongSelf.stateValue.state.title,
|
||||
scheduleTimestamp: strongSelf.stateValue.state.scheduleTimestamp,
|
||||
subscribedToScheduled: strongSelf.stateValue.state.subscribedToScheduled,
|
||||
totalCount: strongSelf.stateValue.state.totalCount,
|
||||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||
isStream: strongSelf.stateValue.state.isStream,
|
||||
upgradedPrivateCallId: strongSelf.stateValue.state.upgradedPrivateCallId,
|
||||
version: strongSelf.stateValue.state.version
|
||||
),
|
||||
overlayState: strongSelf.stateValue.overlayState
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
self.activityRankResetTimer = SwiftSignalKit.Timer(timeout: 10.0, repeat: true, completion: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
@ -1528,6 +1545,7 @@ public final class GroupCallParticipantsContext {
|
||||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||
isStream: strongSelf.stateValue.state.isStream,
|
||||
upgradedPrivateCallId: strongSelf.stateValue.state.upgradedPrivateCallId,
|
||||
version: strongSelf.stateValue.state.version
|
||||
),
|
||||
overlayState: strongSelf.stateValue.overlayState
|
||||
@ -1751,6 +1769,7 @@ public final class GroupCallParticipantsContext {
|
||||
let isVideoEnabled = strongSelf.stateValue.state.isVideoEnabled
|
||||
let isStream = strongSelf.stateValue.state.isStream
|
||||
let unmutedVideoLimit = strongSelf.stateValue.state.unmutedVideoLimit
|
||||
let upgradedPrivateCallId = strongSelf.stateValue.state.upgradedPrivateCallId
|
||||
|
||||
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
||||
|
||||
@ -1770,6 +1789,7 @@ public final class GroupCallParticipantsContext {
|
||||
isVideoEnabled: isVideoEnabled,
|
||||
unmutedVideoLimit: unmutedVideoLimit,
|
||||
isStream: isStream,
|
||||
upgradedPrivateCallId: upgradedPrivateCallId,
|
||||
version: update.version
|
||||
),
|
||||
overlayState: updatedOverlayState
|
||||
@ -2048,12 +2068,15 @@ public final class GroupCallParticipantsContext {
|
||||
}
|
||||
|
||||
public func toggleScheduledSubscription(_ subscribe: Bool) {
|
||||
guard let peerId = self.peerId else {
|
||||
return
|
||||
}
|
||||
if subscribe == self.stateValue.state.subscribedToScheduled {
|
||||
return
|
||||
}
|
||||
self.stateValue.state.subscribedToScheduled = subscribe
|
||||
|
||||
self.subscribeDisposable.set(_internal_toggleScheduledGroupCallSubscription(account: self.account, peerId: self.peerId, callId: self.id, accessHash: self.accessHash, subscribe: subscribe).start())
|
||||
self.subscribeDisposable.set(_internal_toggleScheduledGroupCallSubscription(account: self.account, peerId: peerId, callId: self.id, accessHash: self.accessHash, subscribe: subscribe).start())
|
||||
}
|
||||
|
||||
public func loadMore(token: String) {
|
||||
@ -2370,10 +2393,10 @@ func _internal_cachedGroupCallDisplayAsAvailablePeers(account: Account, peerId:
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_updatedCurrentPeerGroupCall(account: Account, peerId: PeerId) -> Signal<CachedChannelData.ActiveCall?, NoError> {
|
||||
return _internal_fetchAndUpdateCachedPeerData(accountPeerId: account.peerId, peerId: peerId, network: account.network, postbox: account.postbox)
|
||||
func _internal_updatedCurrentPeerGroupCall(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) -> Signal<CachedChannelData.ActiveCall?, NoError> {
|
||||
return _internal_fetchAndUpdateCachedPeerData(accountPeerId: accountPeerId, peerId: peerId, network: network, postbox: postbox)
|
||||
|> mapToSignal { _ -> Signal<CachedChannelData.ActiveCall?, NoError> in
|
||||
return account.postbox.transaction { transaction -> CachedChannelData.ActiveCall? in
|
||||
return postbox.transaction { transaction -> CachedChannelData.ActiveCall? in
|
||||
return (transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData)?.activeCall
|
||||
}
|
||||
}
|
||||
|
@ -57,12 +57,12 @@ public extension TelegramEngine {
|
||||
return _internal_getGroupCallParticipants(account: self.account, callId: callId, accessHash: accessHash, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending)
|
||||
}
|
||||
|
||||
public func joinGroupCall(peerId: PeerId, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||
public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||
return _internal_joinGroupCall(account: self.account, peerId: peerId, joinAs: joinAs, callId: callId, accessHash: accessHash, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash)
|
||||
}
|
||||
|
||||
public func joinGroupCallAsScreencast(peerId: PeerId, callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
||||
return _internal_joinGroupCallAsScreencast(account: self.account, peerId: peerId, callId: callId, accessHash: accessHash, joinPayload: joinPayload)
|
||||
public func joinGroupCallAsScreencast(callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
||||
return _internal_joinGroupCallAsScreencast(account: self.account, callId: callId, accessHash: accessHash, joinPayload: joinPayload)
|
||||
}
|
||||
|
||||
public func leaveGroupCallAsScreencast(callId: Int64, accessHash: Int64) -> Signal<Never, LeaveGroupCallAsScreencastError> {
|
||||
@ -73,7 +73,7 @@ public extension TelegramEngine {
|
||||
return _internal_leaveGroupCall(account: self.account, callId: callId, accessHash: accessHash, source: source)
|
||||
}
|
||||
|
||||
public func stopGroupCall(peerId: PeerId, callId: Int64, accessHash: Int64) -> Signal<Never, StopGroupCallError> {
|
||||
public func stopGroupCall(peerId: PeerId?, callId: Int64, accessHash: Int64) -> Signal<Never, StopGroupCallError> {
|
||||
return _internal_stopGroupCall(account: self.account, peerId: peerId, callId: callId, accessHash: accessHash)
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
public func updatedCurrentPeerGroupCall(peerId: PeerId) -> Signal<EngineGroupCallDescription?, NoError> {
|
||||
return _internal_updatedCurrentPeerGroupCall(account: self.account, peerId: peerId)
|
||||
return _internal_updatedCurrentPeerGroupCall(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId, peerId: peerId)
|
||||
|> map { activeCall -> EngineGroupCallDescription? in
|
||||
return activeCall.flatMap(EngineGroupCallDescription.init)
|
||||
}
|
||||
@ -124,7 +124,7 @@ public extension TelegramEngine {
|
||||
return _internal_getVideoBroadcastPart(dataSource: dataSource, callId: callId, accessHash: accessHash, timestampIdMilliseconds: timestampIdMilliseconds, durationMilliseconds: durationMilliseconds, channelId: channelId, quality: quality)
|
||||
}
|
||||
|
||||
public func groupCall(peerId: PeerId, myPeerId: PeerId, id: Int64, accessHash: Int64, state: GroupCallParticipantsContext.State, previousServiceState: GroupCallParticipantsContext.ServiceState?) -> GroupCallParticipantsContext {
|
||||
public func groupCall(peerId: PeerId?, myPeerId: PeerId, id: Int64, accessHash: Int64, state: GroupCallParticipantsContext.State, previousServiceState: GroupCallParticipantsContext.ServiceState?) -> GroupCallParticipantsContext {
|
||||
return GroupCallParticipantsContext(account: self.account, peerId: peerId, myPeerId: myPeerId, id: id, accessHash: accessHash, state: state, previousServiceState: previousServiceState)
|
||||
}
|
||||
|
||||
|
@ -25,14 +25,16 @@ public enum ResolvePeerResult {
|
||||
}
|
||||
|
||||
func _internal_resolvePeerByName(account: Account, name: String, referrer: String?, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<ResolvePeerIdByNameResult, NoError> {
|
||||
return _internal_resolvePeerByName(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, name: name, referrer: referrer, ageLimit: ageLimit)
|
||||
}
|
||||
|
||||
func _internal_resolvePeerByName(postbox: Postbox, network: Network, accountPeerId: PeerId, name: String, referrer: String?, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<ResolvePeerIdByNameResult, NoError> {
|
||||
var normalizedName = name
|
||||
if normalizedName.hasPrefix("@") {
|
||||
normalizedName = String(normalizedName[name.index(after: name.startIndex)...])
|
||||
}
|
||||
|
||||
let accountPeerId = account.peerId
|
||||
|
||||
return account.postbox.transaction { transaction -> CachedResolvedByNamePeer? in
|
||||
return postbox.transaction { transaction -> CachedResolvedByNamePeer? in
|
||||
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.resolvedByNamePeers, key: CachedResolvedByNamePeer.key(name: normalizedName)))?.get(CachedResolvedByNamePeer.self)
|
||||
}
|
||||
|> mapToSignal { cachedEntry -> Signal<ResolvePeerIdByNameResult, NoError> in
|
||||
@ -45,12 +47,12 @@ func _internal_resolvePeerByName(account: Account, name: String, referrer: Strin
|
||||
flags |= 1 << 0
|
||||
}
|
||||
return .single(.progress)
|
||||
|> then(account.network.request(Api.functions.contacts.resolveUsername(flags: flags, username: normalizedName, referer: referrer))
|
||||
|> then(network.request(Api.functions.contacts.resolveUsername(flags: flags, username: normalizedName, referer: referrer))
|
||||
|> mapError { _ -> Void in
|
||||
return Void()
|
||||
}
|
||||
|> mapToSignal { result -> Signal<ResolvePeerIdByNameResult, Void> in
|
||||
return account.postbox.transaction { transaction -> ResolvePeerIdByNameResult in
|
||||
return postbox.transaction { transaction -> ResolvePeerIdByNameResult in
|
||||
var peerId: PeerId? = nil
|
||||
|
||||
switch result {
|
||||
|
@ -70,7 +70,6 @@ swift_library(
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/UIKitRuntimeUtils",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -885,57 +885,48 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
self.groupCallDisposable = (callManager.currentGroupCallSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] call in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.immediateExperimentalUISettings.conferenceCalls {
|
||||
let mappedCall = call.flatMap(CallController.Call.groupCall)
|
||||
if mappedCall != strongSelf.callController?.call {
|
||||
strongSelf.callController?.dismiss()
|
||||
strongSelf.callController = nil
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
if call !== strongSelf.groupCallController?.call {
|
||||
strongSelf.groupCallController?.dismiss(closing: true, manual: false)
|
||||
strongSelf.groupCallController = nil
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
|
||||
if let call = call, let navigationController = mainWindow.viewController as? NavigationController {
|
||||
mainWindow.hostView.containerView.endEditing(true)
|
||||
|
||||
if let call {
|
||||
mainWindow.hostView.containerView.endEditing(true)
|
||||
|
||||
if call.isStream {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
|
||||
let callController = CallController(sharedContext: strongSelf, account: call.accountContext.account, call: .groupCall(call), easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
|
||||
callController.onViewDidAppear = { [weak strongSelf] in
|
||||
if let strongSelf {
|
||||
let groupCallController = MediaStreamComponentController(call: call)
|
||||
groupCallController.onViewDidAppear = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
}
|
||||
}
|
||||
callController.onViewDidDisappear = { [weak strongSelf] in
|
||||
if let strongSelf {
|
||||
groupCallController.onViewDidDisappear = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(false)
|
||||
}
|
||||
}
|
||||
strongSelf.callController = callController
|
||||
strongSelf.mainWindow?.present(callController, on: .calls)
|
||||
|
||||
strongSelf.hasOngoingCall.set(true)
|
||||
groupCallController.navigationPresentation = .flatModal
|
||||
groupCallController.parentNavigationController = navigationController
|
||||
strongSelf.groupCallController = groupCallController
|
||||
navigationController.pushViewController(groupCallController)
|
||||
} else {
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if call !== strongSelf.groupCallController?.call {
|
||||
strongSelf.groupCallController?.dismiss(closing: true, manual: false)
|
||||
strongSelf.groupCallController = nil
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
|
||||
if let call = call, let navigationController = mainWindow.viewController as? NavigationController {
|
||||
mainWindow.hostView.containerView.endEditing(true)
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
|
||||
if call.isStream {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
let groupCallController = MediaStreamComponentController(call: call)
|
||||
groupCallController.onViewDidAppear = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let _ = (makeVoiceChatControllerInitialData(sharedContext: strongSelf, accountContext: call.accountContext, call: call)
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf, weak navigationController] initialData in
|
||||
guard let strongSelf, let navigationController else {
|
||||
return
|
||||
}
|
||||
|
||||
let groupCallController = makeVoiceChatController(sharedContext: strongSelf, accountContext: call.accountContext, call: call, initialData: initialData)
|
||||
groupCallController.onViewDidAppear = { [weak strongSelf] in
|
||||
if let strongSelf {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
}
|
||||
}
|
||||
groupCallController.onViewDidDisappear = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
groupCallController.onViewDidDisappear = { [weak strongSelf] in
|
||||
if let strongSelf {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(false)
|
||||
}
|
||||
}
|
||||
@ -943,37 +934,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
groupCallController.parentNavigationController = navigationController
|
||||
strongSelf.groupCallController = groupCallController
|
||||
navigationController.pushViewController(groupCallController)
|
||||
} else {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
|
||||
let _ = (makeVoiceChatControllerInitialData(sharedContext: strongSelf, accountContext: call.accountContext, call: call)
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf, weak navigationController] initialData in
|
||||
guard let strongSelf, let navigationController else {
|
||||
return
|
||||
}
|
||||
|
||||
let groupCallController = makeVoiceChatController(sharedContext: strongSelf, accountContext: call.accountContext, call: call, initialData: initialData)
|
||||
groupCallController.onViewDidAppear = { [weak strongSelf] in
|
||||
if let strongSelf {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
}
|
||||
}
|
||||
groupCallController.onViewDidDisappear = { [weak strongSelf] in
|
||||
if let strongSelf {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(false)
|
||||
}
|
||||
}
|
||||
groupCallController.navigationPresentation = .flatModal
|
||||
groupCallController.parentNavigationController = navigationController
|
||||
strongSelf.groupCallController = groupCallController
|
||||
navigationController.pushViewController(groupCallController)
|
||||
})
|
||||
}
|
||||
|
||||
strongSelf.hasOngoingCall.set(true)
|
||||
} else {
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
})
|
||||
}
|
||||
|
||||
strongSelf.hasOngoingCall.set(true)
|
||||
} else {
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,15 +16,18 @@ import ManagedFile
|
||||
import AppBundle
|
||||
|
||||
public struct HLSCodecConfiguration {
|
||||
public var isHardwareAv1Supported: Bool
|
||||
public var isSoftwareAv1Supported: Bool
|
||||
|
||||
public init(isSoftwareAv1Supported: Bool) {
|
||||
public init(isHardwareAv1Supported: Bool, isSoftwareAv1Supported: Bool) {
|
||||
self.isHardwareAv1Supported = isHardwareAv1Supported
|
||||
self.isSoftwareAv1Supported = isSoftwareAv1Supported
|
||||
}
|
||||
}
|
||||
|
||||
public extension HLSCodecConfiguration {
|
||||
init(context: AccountContext) {
|
||||
var isHardwareAv1Supported = internal_isHardwareAv1Supported
|
||||
var isSoftwareAv1Supported = false
|
||||
|
||||
var length: Int = 4
|
||||
@ -34,11 +37,14 @@ public extension HLSCodecConfiguration {
|
||||
isSoftwareAv1Supported = true
|
||||
}
|
||||
|
||||
if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_enable_hardware_av1"] as? Double {
|
||||
isHardwareAv1Supported = value != 0.0
|
||||
}
|
||||
if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_enable_software_av1"] as? Double {
|
||||
isSoftwareAv1Supported = value != 0.0
|
||||
}
|
||||
|
||||
self.init(isSoftwareAv1Supported: isSoftwareAv1Supported)
|
||||
self.init(isHardwareAv1Supported: isHardwareAv1Supported, isSoftwareAv1Supported: isSoftwareAv1Supported)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +58,7 @@ public final class HLSQualitySet {
|
||||
if let alternativeFile = alternativeRepresentation as? TelegramMediaFile {
|
||||
for attribute in alternativeFile.attributes {
|
||||
if case let .Video(_, size, _, _, _, videoCodec) = attribute {
|
||||
if let videoCodec, NativeVideoContent.isVideoCodecSupported(videoCodec: videoCodec, isSoftwareAv1Supported: codecConfiguration.isSoftwareAv1Supported) {
|
||||
if let videoCodec, NativeVideoContent.isVideoCodecSupported(videoCodec: videoCodec, isHardwareAv1Supported: codecConfiguration.isHardwareAv1Supported, isSoftwareAv1Supported: codecConfiguration.isSoftwareAv1Supported) {
|
||||
let key = Int(min(size.width, size.height))
|
||||
if let currentFile = qualityFiles[key] {
|
||||
var currentCodec: String?
|
||||
|
@ -59,7 +59,7 @@ public final class NativeVideoContent: UniversalVideoContent {
|
||||
let displayImage: Bool
|
||||
let hasSentFramesToDisplay: (() -> Void)?
|
||||
|
||||
public static func isVideoCodecSupported(videoCodec: String, isSoftwareAv1Supported: Bool) -> Bool {
|
||||
public static func isVideoCodecSupported(videoCodec: String, isHardwareAv1Supported: Bool, isSoftwareAv1Supported: Bool) -> Bool {
|
||||
if videoCodec == "h264" || videoCodec == "h265" || videoCodec == "avc" || videoCodec == "hevc" {
|
||||
return true
|
||||
}
|
||||
|
@ -463,7 +463,24 @@ public final class OngoingGroupCallContext {
|
||||
|
||||
private let audioSessionActiveDisposable = MetaDisposable()
|
||||
|
||||
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, audioSessionActive: Signal<Bool, NoError>, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, enableSystemMute: Bool, preferX264: Bool, logPath: String, onMutedSpeechActivityDetected: @escaping (Bool) -> Void) {
|
||||
init(
|
||||
queue: Queue,
|
||||
inputDeviceId: String,
|
||||
outputDeviceId: String,
|
||||
audioSessionActive: Signal<Bool, NoError>,
|
||||
video: OngoingCallVideoCapturer?,
|
||||
requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable,
|
||||
rejoinNeeded: @escaping () -> Void,
|
||||
outgoingAudioBitrateKbit: Int32?,
|
||||
videoContentType: VideoContentType,
|
||||
enableNoiseSuppression: Bool,
|
||||
disableAudioInput: Bool,
|
||||
enableSystemMute: Bool,
|
||||
preferX264: Bool,
|
||||
logPath: String,
|
||||
onMutedSpeechActivityDetected: @escaping (Bool) -> Void,
|
||||
encryptionKey: Data?
|
||||
) {
|
||||
self.queue = queue
|
||||
|
||||
#if os(iOS)
|
||||
@ -580,7 +597,8 @@ public final class OngoingGroupCallContext {
|
||||
onMutedSpeechActivityDetected: { value in
|
||||
onMutedSpeechActivityDetected(value)
|
||||
},
|
||||
audioDevice: audioDevice
|
||||
audioDevice: audioDevice,
|
||||
encryptionKey: encryptionKey
|
||||
)
|
||||
#else
|
||||
self.context = GroupCallThreadLocalContext(
|
||||
@ -673,11 +691,11 @@ public final class OngoingGroupCallContext {
|
||||
enableNoiseSuppression: enableNoiseSuppression,
|
||||
disableAudioInput: disableAudioInput,
|
||||
preferX264: preferX264,
|
||||
logPath: logPath
|
||||
logPath: logPath,
|
||||
encryptionKey: encryptionKey
|
||||
)
|
||||
#endif
|
||||
|
||||
|
||||
let queue = self.queue
|
||||
|
||||
let broadcastPartsSource = self.broadcastPartsSource
|
||||
@ -880,125 +898,6 @@ public final class OngoingGroupCallContext {
|
||||
func switchAudioOutput(_ deviceId: String) {
|
||||
self.context.switchAudioOutput(deviceId)
|
||||
}
|
||||
|
||||
func makeIncomingVideoView(endpointId: String, requestClone: Bool, completion: @escaping (OngoingCallContextPresentationCallVideoView?, OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||
self.context.makeIncomingVideoView(withEndpointId: endpointId, requestClone: requestClone, completion: { mainView, cloneView in
|
||||
if let mainView = mainView {
|
||||
#if os(iOS)
|
||||
let mainVideoView = OngoingCallContextPresentationCallVideoView(
|
||||
view: mainView,
|
||||
setOnFirstFrameReceived: { [weak mainView] f in
|
||||
mainView?.setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak mainView] in
|
||||
if let mainView = mainView {
|
||||
return OngoingCallVideoOrientation(mainView.orientation)
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak mainView] in
|
||||
if let mainView = mainView {
|
||||
return mainView.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak mainView] f in
|
||||
mainView?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak mainView] f in
|
||||
mainView?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { [weak mainView] value in
|
||||
mainView?.updateIsEnabled(value)
|
||||
}
|
||||
)
|
||||
var cloneVideoView: OngoingCallContextPresentationCallVideoView?
|
||||
if let cloneView = cloneView {
|
||||
cloneVideoView = OngoingCallContextPresentationCallVideoView(
|
||||
view: cloneView,
|
||||
setOnFirstFrameReceived: { [weak cloneView] f in
|
||||
cloneView?.setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak cloneView] in
|
||||
if let cloneView = cloneView {
|
||||
return OngoingCallVideoOrientation(cloneView.orientation)
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak cloneView] in
|
||||
if let cloneView = cloneView {
|
||||
return cloneView.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak cloneView] f in
|
||||
cloneView?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak cloneView] f in
|
||||
cloneView?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { [weak cloneView] value in
|
||||
cloneView?.updateIsEnabled(value)
|
||||
}
|
||||
)
|
||||
}
|
||||
completion(mainVideoView, cloneVideoView)
|
||||
#else
|
||||
let mainVideoView = OngoingCallContextPresentationCallVideoView(
|
||||
view: mainView,
|
||||
setOnFirstFrameReceived: { [weak mainView] f in
|
||||
mainView?.setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak mainView] in
|
||||
if let mainView = mainView {
|
||||
return OngoingCallVideoOrientation(mainView.orientation)
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak mainView] in
|
||||
if let mainView = mainView {
|
||||
return mainView.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak mainView] f in
|
||||
mainView?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
}, setVideoContentMode: { [weak mainView] mode in
|
||||
mainView?.setVideoContentMode(mode)
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak mainView] f in
|
||||
mainView?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
}, setIsPaused: { [weak mainView] paused in
|
||||
mainView?.setIsPaused(paused)
|
||||
}, renderToSize: { [weak mainView] size, animated in
|
||||
mainView?.render(to: size, animated: animated)
|
||||
}
|
||||
)
|
||||
completion(mainVideoView, nil)
|
||||
#endif
|
||||
} else {
|
||||
completion(nil, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func video(endpointId: String) -> Signal<OngoingGroupCallContext.VideoFrameData, NoError> {
|
||||
let queue = self.queue
|
||||
@ -1113,10 +1012,10 @@ public final class OngoingGroupCallContext {
|
||||
}
|
||||
}
|
||||
|
||||
public init(inputDeviceId: String = "", outputDeviceId: String = "", audioSessionActive: Signal<Bool, NoError>, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, enableSystemMute: Bool, preferX264: Bool, logPath: String, onMutedSpeechActivityDetected: @escaping (Bool) -> Void) {
|
||||
public init(inputDeviceId: String = "", outputDeviceId: String = "", audioSessionActive: Signal<Bool, NoError>, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, enableSystemMute: Bool, preferX264: Bool, logPath: String, onMutedSpeechActivityDetected: @escaping (Bool) -> Void, encryptionKey: Data?) {
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, audioSessionActive: audioSessionActive, video: video, requestMediaChannelDescriptions: requestMediaChannelDescriptions, rejoinNeeded: rejoinNeeded, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: videoContentType, enableNoiseSuppression: enableNoiseSuppression, disableAudioInput: disableAudioInput, enableSystemMute: enableSystemMute, preferX264: preferX264, logPath: logPath, onMutedSpeechActivityDetected: onMutedSpeechActivityDetected)
|
||||
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, audioSessionActive: audioSessionActive, video: video, requestMediaChannelDescriptions: requestMediaChannelDescriptions, rejoinNeeded: rejoinNeeded, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: videoContentType, enableNoiseSuppression: enableNoiseSuppression, disableAudioInput: disableAudioInput, enableSystemMute: enableSystemMute, preferX264: preferX264, logPath: logPath, onMutedSpeechActivityDetected: onMutedSpeechActivityDetected, encryptionKey: encryptionKey)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1209,12 +1108,6 @@ public final class OngoingGroupCallContext {
|
||||
impl.stop()
|
||||
}
|
||||
}
|
||||
|
||||
public func makeIncomingVideoView(endpointId: String, requestClone: Bool, completion: @escaping (OngoingCallContextPresentationCallVideoView?, OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||
self.impl.with { impl in
|
||||
impl.makeIncomingVideoView(endpointId: endpointId, requestClone: requestClone, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public func video(endpointId: String) -> Signal<OngoingGroupCallContext.VideoFrameData, NoError> {
|
||||
return Signal { subscriber in
|
||||
|
@ -1295,54 +1295,6 @@ public final class OngoingCallContext {
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public func makeIncomingVideoView(completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||
self.withContext { context in
|
||||
if let context = context as? OngoingCallThreadLocalContextWebrtc {
|
||||
context.makeIncomingVideoView { view in
|
||||
if let view = view {
|
||||
completion(OngoingCallContextPresentationCallVideoView(
|
||||
view: view,
|
||||
setOnFirstFrameReceived: { [weak view] f in
|
||||
view?.setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
return OngoingCallVideoOrientation(view.orientation)
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak view] f in
|
||||
view?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak view] f in
|
||||
view?.setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
},
|
||||
updateIsEnabled: { [weak view] value in
|
||||
view?.updateIsEnabled(value)
|
||||
}
|
||||
))
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func addExternalAudioData(data: Data) {
|
||||
self.withContext { context in
|
||||
|
@ -416,7 +416,8 @@ typedef NS_ENUM(int32_t, OngoingGroupCallRequestedVideoQuality) {
|
||||
preferX264:(bool)preferX264
|
||||
logPath:(NSString * _Nonnull)logPath
|
||||
onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDetected
|
||||
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice;
|
||||
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
|
||||
encryptionKey:(NSData * _Nullable)encryptionKey;
|
||||
|
||||
- (void)stop;
|
||||
|
||||
|
@ -1695,7 +1695,8 @@ private:
|
||||
preferX264:(bool)preferX264
|
||||
logPath:(NSString * _Nonnull)logPath
|
||||
onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDetected
|
||||
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice {
|
||||
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
|
||||
encryptionKey:(NSData * _Nullable)encryptionKey {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = queue;
|
||||
@ -1760,6 +1761,14 @@ audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice {
|
||||
tgcalls::GroupConfig config;
|
||||
config.need_log = true;
|
||||
config.logPath.data = std::string(logPath.length == 0 ? "" : logPath.UTF8String);
|
||||
|
||||
std::optional<tgcalls::EncryptionKey> mappedEncryptionKey;
|
||||
if (encryptionKey) {
|
||||
auto encryptionKeyValue = std::make_shared<std::array<uint8_t, 256>>();
|
||||
memcpy(encryptionKeyValue->data(), encryptionKey.bytes, encryptionKey.length);
|
||||
|
||||
mappedEncryptionKey = tgcalls::EncryptionKey(encryptionKeyValue, true);
|
||||
}
|
||||
|
||||
__weak GroupCallThreadLocalContext *weakSelf = self;
|
||||
_instance.reset(new tgcalls::GroupInstanceCustomImpl((tgcalls::GroupInstanceDescriptor){
|
||||
@ -1958,7 +1967,8 @@ audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice {
|
||||
strongSelf->_onMutedSpeechActivityDetected(value);
|
||||
}
|
||||
}];
|
||||
}
|
||||
},
|
||||
.encryptionKey = mappedEncryptionKey
|
||||
}));
|
||||
}
|
||||
return self;
|
||||
|
Loading…
x
Reference in New Issue
Block a user