diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSource.swift b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSource.swift index 3c86dc17ca..83fb2ab4e5 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSource.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSource.swift @@ -283,6 +283,9 @@ public final class FFMpegMediaFrameSource: NSObject, MediaFrameSource { let _ = currentSemaphore.swap(nil) subscriber.putError(.generic) } + } else { + let _ = currentSemaphore.swap(nil) + subscriber.putError(.generic) } } }) diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift index 14c0330554..76037e0848 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaVideoFrameDecoder.swift @@ -1,10 +1,30 @@ + +#if !os(macOS) import UIKit +#else +import AppKit +#endif import CoreMedia import Accelerate import FFMpegBinding private let bufferCount = 32 + + +#if os(macOS) +private let deviceColorSpace: CGColorSpace = { + if #available(OSX 10.11.2, *) { + if let colorSpace = CGColorSpace(name: CGColorSpace.displayP3) { + return colorSpace + } else { + return CGColorSpaceCreateDeviceRGB() + } + } else { + return CGColorSpaceCreateDeviceRGB() + } +}() +#else private let deviceColorSpace: CGColorSpace = { if #available(iOSApplicationExtension 9.3, iOS 9.3, *) { if let colorSpace = CGColorSpace(name: CGColorSpace.displayP3) { @@ -16,7 +36,7 @@ private let deviceColorSpace: CGColorSpace = { return CGColorSpaceCreateDeviceRGB() } }() - +#endif public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { public enum ReceiveResult { case error diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index 9882fcd31b..835d77c19f 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -73,8 +73,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case optimizeDatabase(PresentationTheme) case photoPreview(PresentationTheme, Bool) case knockoutWallpaper(PresentationTheme, Bool) - case demoAudioStream(Bool) - case snapPinListToTop(Bool) + case demoVideoChats(Bool) case playerEmbedding(Bool) case playlistPlayback(Bool) case voiceConference @@ -94,7 +93,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.logging.rawValue case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: return DebugControllerSection.experiments.rawValue - case .clearTips, .reimport, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .demoAudioStream, .snapPinListToTop, .playerEmbedding, .playlistPlayback, .voiceConference: + case .clearTips, .reimport, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .demoVideoChats, .playerEmbedding, .playlistPlayback, .voiceConference: return DebugControllerSection.experiments.rawValue case .preferredVideoCodec: return DebugControllerSection.videoExperiments.rawValue @@ -155,10 +154,8 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 22 case .knockoutWallpaper: return 23 - case .demoAudioStream: + case .demoVideoChats: return 24 - case .snapPinListToTop: - return 25 case .playerEmbedding: return 26 case .playlistPlayback: @@ -706,22 +703,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) - case let .demoAudioStream(value): - return ItemListSwitchItem(presentationData: presentationData, title: "Demo Audio Stream", value: value, sectionId: self.section, style: .blocks, updated: { value in + case let .demoVideoChats(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Demo Video", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings - settings.demoAudioStream = value - return settings - }) - }).start() - }) - case let .snapPinListToTop(value): - return ItemListSwitchItem(presentationData: presentationData, title: "Pin List Top Edge", value: value, sectionId: self.section, style: .blocks, updated: { value in - let _ = arguments.sharedContext.accountManager.transaction ({ transaction in - transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in - var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings - settings.snapPinListToTop = value + settings.demoVideoChats = value return settings }) }).start() @@ -824,10 +811,8 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS entries.append(.resetHoles(presentationData.theme)) entries.append(.reindexUnread(presentationData.theme)) entries.append(.optimizeDatabase(presentationData.theme)) - //entries.append(.photoPreview(presentationData.theme, experimentalSettings.chatListPhotos)) entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper)) - entries.append(.demoAudioStream(experimentalSettings.demoAudioStream)) - entries.append(.snapPinListToTop(experimentalSettings.snapPinListToTop)) + entries.append(.demoVideoChats(experimentalSettings.demoVideoChats)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 7e1902600a..26778f64de 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -142,7 +142,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) } dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) } dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) } - dict[430815881] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) } + dict[-1184160274] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) } dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) } dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) } dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) } diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 9466f52bb2..8ddcfa52dc 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -3604,13 +3604,13 @@ public extension Api { } public enum GroupCallParticipant: TypeConstructorDescription { - case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?, raiseHandRating: Int64?) + case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?, raiseHandRating: Int64?, params: Api.DataJSON?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating): + case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let params): if boxed { - buffer.appendInt32(430815881) + buffer.appendInt32(-1184160274) } serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) @@ -3620,14 +3620,15 @@ public extension Api { if Int(flags) & Int(1 << 7) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 11) != 0 {serializeString(about!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 13) != 0 {serializeInt64(raiseHandRating!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {params!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating): - return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about), ("raiseHandRating", raiseHandRating)]) + case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about, let raiseHandRating, let params): + return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about), ("raiseHandRating", raiseHandRating), ("params", params)]) } } @@ -3650,6 +3651,10 @@ public extension Api { if Int(_1!) & Int(1 << 11) != 0 {_7 = parseString(reader) } var _8: Int64? if Int(_1!) & Int(1 << 13) != 0 {_8 = reader.readInt64() } + var _9: Api.DataJSON? + if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.DataJSON + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -3658,8 +3663,9 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 13) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8) + let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8, params: _9) } else { return nil diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 17edf96871..eceae9d0c4 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -571,8 +571,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.statePromise = ValuePromise(self.stateValue) self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - - //self.videoCapturer = OngoingCallVideoCapturer(keepLandscape: true) + + if accountContext.sharedContext.immediateExperimentalUISettings.demoVideoChats { + self.videoCapturer = OngoingCallVideoCapturer(keepLandscape: false) + } self.isVideo = self.videoCapturer != nil var didReceiveAudioOutputs = false @@ -1039,6 +1041,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if let current = self.callContext { callContext = current } else { + var outgoingAudioBitrateKbit: Int32? + let appConfiguration = self.accountContext.currentAppConfiguration.with({ $0 }) + if let data = appConfiguration.data, let value = data["voice_chat_send_bitrate"] as? Int32 { + outgoingAudioBitrateKbit = value + } + callContext = OngoingGroupCallContext(video: self.videoCapturer, participantDescriptionsRequired: { [weak self] ssrcs in Queue.mainQueue().async { guard let strongSelf = self else { @@ -1055,7 +1063,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.requestCall(movingFromBroadcastToRtc: false) } } - }) + }, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, enableVideo: self.isVideo) self.incomingVideoSourcePromise.set(callContext.videoSources |> deliverOnMainQueue |> map { [weak self] sources -> [PeerId: UInt32] in diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 7a7050b2f1..7f6d4f2e7e 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -178,6 +178,8 @@ final class GroupVideoNode: ASDisplayNode { self.videoViewContainer.addSubview(self.videoView.view) self.view.addSubview(self.videoViewContainer) + self.clipsToBounds = true + videoView.setOnFirstFrameReceived({ [weak self] _ in Queue.mainQueue().async { guard let strongSelf = self else { @@ -270,6 +272,7 @@ private final class MainVideoContainerNode: ASDisplayNode { private let call: PresentationGroupCall private var currentVideoNode: GroupVideoNode? + private var candidateVideoNode: GroupVideoNode? private var currentPeer: (PeerId, UInt32)? private var validLayout: CGSize? @@ -283,7 +286,7 @@ private final class MainVideoContainerNode: ASDisplayNode { self.backgroundColor = .black } - func updatePeer(peer: (peerId: PeerId, source: UInt32)?) { + func updatePeer(peer: (peerId: PeerId, source: UInt32)?, waitForFullSize: Bool) { if self.currentPeer?.0 == peer?.0 && self.currentPeer?.1 == peer?.1 { return } @@ -294,15 +297,40 @@ private final class MainVideoContainerNode: ASDisplayNode { guard let strongSelf = self, let videoView = videoView else { return } - let videoNode = GroupVideoNode(videoView: videoView) - if let currentVideoNode = strongSelf.currentVideoNode { - currentVideoNode.removeFromSupernode() - strongSelf.currentVideoNode = nil - } - strongSelf.currentVideoNode = videoNode - strongSelf.addSubnode(videoNode) - if let size = strongSelf.validLayout { - strongSelf.update(size: size, transition: .immediate) + + if waitForFullSize { + let candidateVideoNode = GroupVideoNode(videoView: videoView) + strongSelf.candidateVideoNode = candidateVideoNode + + Queue.mainQueue().after(0.3, { [weak candidateVideoNode] in + guard let strongSelf = self, let videoNode = candidateVideoNode, videoNode === strongSelf.candidateVideoNode else { + return + } + + if let currentVideoNode = strongSelf.currentVideoNode { + currentVideoNode.removeFromSupernode() + strongSelf.currentVideoNode = nil + } + strongSelf.currentVideoNode = videoNode + strongSelf.addSubnode(videoNode) + if let size = strongSelf.validLayout { + strongSelf.update(size: size, transition: .immediate) + } + }) + } else { + strongSelf.candidateVideoNode = nil + + let videoNode = GroupVideoNode(videoView: videoView) + + if let currentVideoNode = strongSelf.currentVideoNode { + currentVideoNode.removeFromSupernode() + strongSelf.currentVideoNode = nil + } + strongSelf.currentVideoNode = videoNode + strongSelf.addSubnode(videoNode) + if let size = strongSelf.validLayout { + strongSelf.update(size: size, transition: .immediate) + } } } }) @@ -778,9 +806,9 @@ public final class VoiceChatController: ViewController { self.backgroundNode.backgroundColor = secondaryPanelBackgroundColor self.backgroundNode.clipsToBounds = false - /*if false { + if sharedContext.immediateExperimentalUISettings.demoVideoChats { self.mainVideoContainer = MainVideoContainerNode(context: call.accountContext, call: call) - }*/ + } self.listNode = ListView() self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3) @@ -874,12 +902,6 @@ public final class VoiceChatController: ViewController { let _ = self?.call.updateMuteState(peerId: peerId, isMuted: isMuted) }, openPeer: { [weak self] peerId in if let strongSelf = self { - /*let context = strongSelf.context - strongSelf.controller?.dismiss(completion: { - Queue.mainQueue().justDispatch { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), keepStack: .always, purposefulAction: {}, peekData: nil)) - } - })*/ for entry in strongSelf.currentEntries { switch entry { case let .peer(peer): @@ -888,11 +910,11 @@ public final class VoiceChatController: ViewController { if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source { strongSelf.currentDominantSpeakerWithVideo = (peerId, source) strongSelf.call.setFullSizeVideo(peerId: peerId) - strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source)) + strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source), waitForFullSize: false) } else { strongSelf.currentDominantSpeakerWithVideo = nil strongSelf.call.setFullSizeVideo(peerId: nil) - strongSelf.mainVideoContainer?.updatePeer(peer: nil) + strongSelf.mainVideoContainer?.updatePeer(peer: nil, waitForFullSize: false) } } } @@ -1182,17 +1204,19 @@ public final class VoiceChatController: ViewController { } }), true)) } - - /*items.append(.action(ContextMenuActionItem(text: "Toggle Full Screen", icon: { theme in - return nil - }, action: { _, f in - guard let strongSelf = self else { - return - } - - strongSelf.itemInteraction?.openPeer(peer.id) - f(.default) - })))*/ + + if strongSelf.context.sharedContext.immediateExperimentalUISettings.demoVideoChats { + items.append(.action(ContextMenuActionItem(text: "Toggle Full Screen", icon: { theme in + return nil + }, action: { _, f in + guard let strongSelf = self else { + return + } + + strongSelf.itemInteraction?.openPeer(peer.id) + f(.default) + }))) + } if peer.id == strongSelf.callState?.myPeerId { if entry.raisedHand { @@ -1598,7 +1622,7 @@ public final class VoiceChatController: ViewController { if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != source { strongSelf.currentDominantSpeakerWithVideo = (peerId, source) strongSelf.call.setFullSizeVideo(peerId: peerId) - strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source)) + strongSelf.mainVideoContainer?.updatePeer(peer: (peerId: peerId, source: source), waitForFullSize: true) } } @@ -1750,7 +1774,7 @@ public final class VoiceChatController: ViewController { if !validSources.contains(source) { strongSelf.currentDominantSpeakerWithVideo = nil strongSelf.call.setFullSizeVideo(peerId: nil) - strongSelf.mainVideoContainer?.updatePeer(peer: nil) + strongSelf.mainVideoContainer?.updatePeer(peer: nil, waitForFullSize: false) } } @@ -2534,7 +2558,7 @@ public final class VoiceChatController: ViewController { let topPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelOffset), size: CGSize(width: size.width, height: topPanelHeight)) if let mainVideoContainer = self.mainVideoContainer { - let videoContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: layout.size.width, height: 200.0)) + let videoContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: layout.size.width, height: min(300.0, layout.size.width))) transition.updateFrameAdditive(node: mainVideoContainer, frame: videoContainerFrame) mainVideoContainer.update(size: videoContainerFrame.size, transition: transition) } @@ -2805,7 +2829,7 @@ public final class VoiceChatController: ViewController { let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom var listTopInset = layoutTopInset + topPanelHeight if self.mainVideoContainer != nil { - listTopInset += 200.0 + listTopInset += min(300.0, layout.size.width) } let listSize = CGSize(width: size.width, height: layout.size.height - listTopInset - bottomPanelHeight) diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index 0f2baad395..7954809f98 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -110,7 +110,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int loop: for participant in participants { switch participant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -134,13 +134,13 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int } else if mutedByYou { muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou) } - let jsonParams: String? = nil - /*if let params = params { + var jsonParams: String? = nil + if let params = params { switch params { case let .dataJSON(data): jsonParams = data } - }*/ + } parsedParticipants.append(GroupCallParticipantsContext.Participant( peer: peer, ssrc: ssrc, @@ -297,7 +297,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash loop: for participant in participants { switch participant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -320,13 +320,13 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash } else if mutedByYou { muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou) } - let jsonParams: String? = nil - /*if let params = params { + var jsonParams: String? = nil + if let params = params { switch params { case let .dataJSON(data): jsonParams = data } - }*/ + } parsedParticipants.append(GroupCallParticipantsContext.Participant( peer: peer, ssrc: ssrc, @@ -525,7 +525,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal case let .updateGroupCallParticipants(_, participants, _): loop: for participant in participants { switch participant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -548,13 +548,13 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal } else if mutedByYou { muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: mutedByYou) } - let jsonParams: String? = nil - /*if let params = params { + var jsonParams: String? = nil + if let params = params { switch params { case let .dataJSON(data): jsonParams = data } - }*/ + } if !state.participants.contains(where: { $0.peer.id == peer.id }) { state.participants.append(GroupCallParticipantsContext.Participant( peer: peer, @@ -797,6 +797,12 @@ public final class GroupCallParticipantsContext { } public static func compare(lhs: Participant, rhs: Participant, sortAscending: Bool) -> Bool { + let lhsCanUnmute = lhs.muteState?.canUnmute ?? true + let rhsCanUnmute = rhs.muteState?.canUnmute ?? true + if lhsCanUnmute != rhsCanUnmute { + return lhsCanUnmute + } + if let lhsActivityRank = lhs.activityRank, let rhsActivityRank = rhs.activityRank { if lhsActivityRank != rhsActivityRank { return lhsActivityRank < rhsActivityRank @@ -1440,6 +1446,11 @@ public final class GroupCallParticipantsContext { activityTimestamp = participantUpdate.activityTimestamp ?? previousActivityTimestamp } + if let muteState = participantUpdate.muteState, !muteState.canUnmute { + previousActivityRank = nil + activityTimestamp = nil + } + var volume = participantUpdate.volume var muteState = participantUpdate.muteState if participantUpdate.isMin { @@ -1720,7 +1731,7 @@ public final class GroupCallParticipantsContext { extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { init(_ apiParticipant: Api.GroupCallParticipant) { switch apiParticipant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -1753,13 +1764,13 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { participationStatusChange = .none } - let jsonParams: String? = nil - /*if let params = params { + var jsonParams: String? = nil + if let params = params { switch params { case let .dataJSON(data): jsonParams = data } - }*/ + } self.init( peerId: peerId, @@ -1783,7 +1794,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate { var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = [] for participant in participants { switch participant { - case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating): + case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about, raiseHandRating, params): let peerId: PeerId switch apiPeerId { case let .peerUser(userId): @@ -1816,13 +1827,13 @@ extension GroupCallParticipantsContext.Update.StateUpdate { participationStatusChange = .none } - let jsonParams: String? = nil - /*if let params = params { + var jsonParams: String? = nil + if let params = params { switch params { case let .dataJSON(data): jsonParams = data } - }*/ + } participantUpdates.append(GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate( peerId: peerId, diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift index 8a525de035..9e06595675 100644 --- a/submodules/TelegramCore/Sources/Serialization.swift +++ b/submodules/TelegramCore/Sources/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 126 + return 127 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 0193a6f6bd..132a741d7e 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3722,13 +3722,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var isScrolled: Bool } - let messageRangeEdge: Signal = self.context.sharedContext.accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.experimentalUISettings])) - |> map { sharedData -> Bool in - let experimentalSettings: ExperimentalUISettings = (sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings) ?? ExperimentalUISettings.defaultSettings - return experimentalSettings.snapPinListToTop - } - |> distinctUntilChanged - let referenceMessage: Signal if latest { referenceMessage = .single(nil) @@ -3736,16 +3729,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G referenceMessage = combineLatest( queue: Queue.mainQueue(), self.scrolledToMessageId.get(), - self.chatDisplayNode.historyNode.topVisibleMessageRange.get(), - messageRangeEdge + self.chatDisplayNode.historyNode.topVisibleMessageRange.get() ) - |> map { scrolledToMessageId, topVisibleMessageRange, messageRangeEdge -> ReferenceMessage? in + |> map { scrolledToMessageId, topVisibleMessageRange -> ReferenceMessage? in let topVisibleMessage: MessageId? - if messageRangeEdge { - topVisibleMessage = topVisibleMessageRange?.lowerBound - } else { - topVisibleMessage = topVisibleMessageRange?.upperBound - } + topVisibleMessage = topVisibleMessageRange?.upperBound if let scrolledToMessageId = scrolledToMessageId { if let topVisibleMessage = topVisibleMessage { diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index d685310c04..f5e71e32b4 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -14,8 +14,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { public var preferredVideoCodec: String? public var disableVideoAspectScaling: Bool public var enableVoipTcp: Bool - public var snapPinListToTop: Bool - public var demoAudioStream: Bool + public var demoVideoChats: Bool public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -30,8 +29,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { preferredVideoCodec: nil, disableVideoAspectScaling: false, enableVoipTcp: false, - snapPinListToTop: false, - demoAudioStream: false + demoVideoChats: false ) } @@ -47,8 +45,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { preferredVideoCodec: String?, disableVideoAspectScaling: Bool, enableVoipTcp: Bool, - snapPinListToTop: Bool, - demoAudioStream: Bool + demoVideoChats: Bool ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -61,8 +58,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.preferredVideoCodec = preferredVideoCodec self.disableVideoAspectScaling = disableVideoAspectScaling self.enableVoipTcp = enableVoipTcp - self.snapPinListToTop = snapPinListToTop - self.demoAudioStream = demoAudioStream + self.demoVideoChats = demoVideoChats } public init(decoder: PostboxDecoder) { @@ -77,8 +73,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.preferredVideoCodec = decoder.decodeOptionalStringForKey("preferredVideoCodec") self.disableVideoAspectScaling = decoder.decodeInt32ForKey("disableVideoAspectScaling", orElse: 0) != 0 self.enableVoipTcp = decoder.decodeInt32ForKey("enableVoipTcp", orElse: 0) != 0 - self.snapPinListToTop = decoder.decodeInt32ForKey("snapPinListToTop", orElse: 0) != 0 - self.demoAudioStream = decoder.decodeInt32ForKey("demoAudioStream", orElse: 0) != 0 + self.demoVideoChats = decoder.decodeInt32ForKey("demoVideoChats", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { @@ -95,8 +90,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { } encoder.encodeInt32(self.disableVideoAspectScaling ? 1 : 0, forKey: "disableVideoAspectScaling") encoder.encodeInt32(self.enableVoipTcp ? 1 : 0, forKey: "enableVoipTcp") - encoder.encodeInt32(self.snapPinListToTop ? 1 : 0, forKey: "snapPinListToTop") - encoder.encodeInt32(self.demoAudioStream ? 1 : 0, forKey: "demoAudioStream") + encoder.encodeInt32(self.demoVideoChats ? 1 : 0, forKey: "demoVideoChats") } public func isEqual(to: PreferencesEntry) -> Bool { diff --git a/submodules/TelegramVoip/Sources/GroupCallContext.swift b/submodules/TelegramVoip/Sources/GroupCallContext.swift index 8a292348d9..f41298e6b5 100644 --- a/submodules/TelegramVoip/Sources/GroupCallContext.swift +++ b/submodules/TelegramVoip/Sources/GroupCallContext.swift @@ -179,7 +179,7 @@ public final class OngoingGroupCallContext { private var broadcastPartsSource: BroadcastPartSource? - init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void) { + init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, enableVideo: Bool) { self.queue = queue var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)? @@ -220,7 +220,9 @@ public final class OngoingGroupCallContext { } return OngoingGroupCallBroadcastPartTaskImpl(disposable: disposable) - } + }, + outgoingAudioBitrateKbit: outgoingAudioBitrateKbit ?? 32, + enableVideo: enableVideo ) let queue = self.queue @@ -508,10 +510,10 @@ public final class OngoingGroupCallContext { } } - public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void) { + public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set) -> Void, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, enableVideo: Bool) { let queue = self.queue self.impl = QueueLocalObject(queue: queue, generate: { - return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, participantDescriptionsRequired: participantDescriptionsRequired, audioStreamData: audioStreamData, rejoinNeeded: rejoinNeeded) + return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, participantDescriptionsRequired: participantDescriptionsRequired, audioStreamData: audioStreamData, rejoinNeeded: rejoinNeeded, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, enableVideo: enableVideo) }) } diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h index 9bc06c6265..d81ac6e1df 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h @@ -196,7 +196,7 @@ typedef NS_ENUM(int32_t, OngoingGroupCallBroadcastPartStatus) { @interface GroupCallThreadLocalContext : NSObject -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart; +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart outgoingAudioBitrateKbit:(int32_t)outgoingAudioBitrateKbit enableVideo:(bool)enableVideo; - (void)stop; diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 948fd87723..1973c5ec0a 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -854,7 +854,7 @@ private: @implementation GroupCallThreadLocalContext -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart { +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray * _Nonnull))participantDescriptionsRequired requestBroadcastPart:(id _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart outgoingAudioBitrateKbit:(int32_t)outgoingAudioBitrateKbit enableVideo:(bool)enableVideo { self = [super init]; if (self != nil) { _queue = queue; @@ -937,7 +937,9 @@ private: completion(std::move(parsedPart)); }); return std::make_shared(task); - } + }, + .outgoingAudioBitrateKbit = outgoingAudioBitrateKbit, + .enableVideo = enableVideo })); } return self; @@ -1232,6 +1234,22 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn } parsedParticipants.push_back(parsedParticipant); } + + NSDictionary *video = dict[@"video"]; + if ([video isKindOfClass:[NSDictionary class]]) { + NSArray *serverSources = video[@"server_sources"]; + if ([serverSources isKindOfClass:[NSArray class]]) { + for (NSNumber *sourceNumber in serverSources) { + if ([sourceNumber isKindOfClass:[NSNumber class]]) { + int32_t signedSource = [sourceNumber intValue]; + result.serverVideoBandwidthProbingSsrc = *(int32_t *)&signedSource; + } else if ([sourceNumber isKindOfClass:[NSString class]]) { + uint32_t source = (uint32_t)[sourceNumber longLongValue]; + result.serverVideoBandwidthProbingSsrc = source; + } + } + } + } if (_instance) { _instance->setJoinResponsePayload(result, std::move(parsedParticipants)); diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 194449a2dd..68ade13752 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 194449a2dd5a2bcb56a487e691842111bf5adf5f +Subproject commit 68ade13752f14fecf0b32bc08e8e47164ef52ddc