[WIP] Conference calls

This commit is contained in:
Isaac 2024-12-10 22:17:36 +08:00
parent 166df8406f
commit 7e9b1fcc40
34 changed files with 1199 additions and 1099 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -70,7 +70,6 @@ swift_library(
"//submodules/AppBundle",
"//submodules/UIKitRuntimeUtils",
"//submodules/TelegramPresentationData",
"//submodules/Components/MultilineTextComponent",
],
visibility = [
"//visibility:public",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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