diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index a00293abb5..4209a92e08 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -72,7 +72,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case alternativeFolderTabs(Bool) case playerEmbedding(Bool) case playlistPlayback(Bool) - case enableHighBitrateVideoCalls(Bool) + case preferredVideoCodec(Int, String, String?, Bool) case hostInfo(PresentationTheme, String) case versionInfo(PresentationTheme) @@ -88,14 +88,14 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.experiments.rawValue case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback: return DebugControllerSection.experiments.rawValue - case .enableHighBitrateVideoCalls: + case .preferredVideoCodec: return DebugControllerSection.videoExperiments.rawValue case .hostInfo, .versionInfo: return DebugControllerSection.info.rawValue } } - var stableId: Int32 { + var stableId: Int { switch self { case .sendLogs: return 0 @@ -147,12 +147,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 24 case .playlistPlayback: return 25 - case .enableHighBitrateVideoCalls: - return 26 + case let .preferredVideoCodec(index, _, _, _): + return 26 + index case .hostInfo: - return 29 + return 100 case .versionInfo: - return 30 + return 101 } } @@ -570,12 +570,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) - case let .enableHighBitrateVideoCalls(value): - return ItemListSwitchItem(presentationData: presentationData, title: "HD Video Calls", value: value, sectionId: self.section, style: .blocks, updated: { value in + case let .preferredVideoCodec(_, title, value, isSelected): + return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .right, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: { let _ = arguments.sharedContext.accountManager.transaction ({ transaction in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings - settings.enableHighBitrateVideoCalls = value + settings.preferredVideoCodec = value return settings }) }).start() @@ -625,7 +625,18 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS entries.append(.alternativeFolderTabs(experimentalSettings.foldersTabAtBottom)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) - entries.append(.enableHighBitrateVideoCalls(experimentalSettings.enableHighBitrateVideoCalls)) + + let codecs: [(String, String?)] = [ + ("No Preference", nil), + ("H265", "H265"), + ("H264", "H264"), + ("VP8", "VP8"), + ("VP9", "VP9") + ] + + for i in 0 ..< codecs.count { + entries.append(.preferredVideoCodec(i, codecs[i].0, codecs[i].1, experimentalSettings.preferredVideoCodec == codecs[i].1)) + } if let backupHostOverride = networkSettings?.backupHostOverride { entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)")) diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index 3a8a21f31c..250459dcdf 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -169,7 +169,7 @@ public final class PresentationCallImpl: PresentationCall { public let isOutgoing: Bool public var isVideo: Bool public var isVideoPossible: Bool - public let enableHighBitrateVideoCalls: Bool + public let preferredVideoCodec: String? public let peer: Peer? private let serializedData: String? @@ -265,7 +265,7 @@ public final class PresentationCallImpl: PresentationCall { updatedNetworkType: Signal, startWithVideo: Bool, isVideoPossible: Bool, - enableHighBitrateVideoCalls: Bool + preferredVideoCodec: String? ) { self.account = account self.audioSession = audioSession @@ -292,9 +292,9 @@ public final class PresentationCallImpl: PresentationCall { self.isOutgoing = isOutgoing self.isVideo = initialState?.type == .video self.isVideoPossible = isVideoPossible + self.preferredVideoCodec = preferredVideoCodec self.peer = peer self.isVideo = startWithVideo - self.enableHighBitrateVideoCalls = enableHighBitrateVideoCalls if self.isVideo { self.videoCapturer = OngoingCallVideoCapturer() self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal)) @@ -606,7 +606,7 @@ public final class PresentationCallImpl: PresentationCall { if let _ = audioSessionControl, !wasActive || previousControl == nil { let logName = "\(id.id)_\(id.accessHash)" - let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableHighBitrateVideoCalls: self.enableHighBitrateVideoCalls, audioSessionActive: self.audioSessionActive.get(), logName: logName) + let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec) self.ongoingContext = ongoingContext ongoingContext.setIsMuted(self.isMutedValue) diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift index 2e55dde604..6c99477092 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift @@ -314,7 +314,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { updatedNetworkType: firstState.0.networkType, startWithVideo: firstState.2.isVideo, isVideoPossible: firstState.2.isVideoPossible, - enableHighBitrateVideoCalls: experimentalSettings.enableHighBitrateVideoCalls + preferredVideoCodec: experimentalSettings.preferredVideoCodec ) strongSelf.updateCurrentCall(call) strongSelf.currentCallPromise.set(.single(call)) @@ -556,7 +556,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { updatedNetworkType: account.networkType, startWithVideo: isVideo, isVideoPossible: isVideoPossible, - enableHighBitrateVideoCalls: experimentalSettings.enableHighBitrateVideoCalls + preferredVideoCodec: experimentalSettings.preferredVideoCodec ) strongSelf.updateCurrentCall(call) strongSelf.currentCallPromise.set(.single(call)) diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 76eefad9aa..c8a02de15f 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -9,9 +9,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { public var chatListPhotos: Bool public var knockoutWallpaper: Bool public var foldersTabAtBottom: Bool - public var enableHighBitrateVideoCalls: Bool public var playerEmbedding: Bool public var playlistPlayback: Bool + public var preferredVideoCodec: String? public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -21,9 +21,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { chatListPhotos: false, knockoutWallpaper: false, foldersTabAtBottom: false, - enableHighBitrateVideoCalls: false, playerEmbedding: false, - playlistPlayback: false + playlistPlayback: false, + preferredVideoCodec: nil ) } @@ -34,9 +34,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { chatListPhotos: Bool, knockoutWallpaper: Bool, foldersTabAtBottom: Bool, - enableHighBitrateVideoCalls: Bool, playerEmbedding: Bool, - playlistPlayback: Bool + playlistPlayback: Bool, + preferredVideoCodec: String? ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -44,9 +44,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.chatListPhotos = chatListPhotos self.knockoutWallpaper = knockoutWallpaper self.foldersTabAtBottom = foldersTabAtBottom - self.enableHighBitrateVideoCalls = enableHighBitrateVideoCalls self.playerEmbedding = playerEmbedding self.playlistPlayback = playlistPlayback + self.preferredVideoCodec = preferredVideoCodec } public init(decoder: PostboxDecoder) { @@ -56,9 +56,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.chatListPhotos = decoder.decodeInt32ForKey("chatListPhotos", orElse: 0) != 0 self.knockoutWallpaper = decoder.decodeInt32ForKey("knockoutWallpaper", orElse: 0) != 0 self.foldersTabAtBottom = decoder.decodeInt32ForKey("foldersTabAtBottom", orElse: 0) != 0 - self.enableHighBitrateVideoCalls = decoder.decodeInt32ForKey("enableHighBitrateVideoCalls", orElse: 0) != 0 self.playerEmbedding = decoder.decodeInt32ForKey("playerEmbedding", orElse: 0) != 0 self.playlistPlayback = decoder.decodeInt32ForKey("playlistPlayback", orElse: 0) != 0 + self.preferredVideoCodec = decoder.decodeOptionalStringForKey("preferredVideoCodec") } public func encode(_ encoder: PostboxEncoder) { @@ -68,9 +68,11 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { encoder.encodeInt32(self.chatListPhotos ? 1 : 0, forKey: "chatListPhotos") encoder.encodeInt32(self.knockoutWallpaper ? 1 : 0, forKey: "knockoutWallpaper") encoder.encodeInt32(self.foldersTabAtBottom ? 1 : 0, forKey: "foldersTabAtBottom") - encoder.encodeInt32(self.enableHighBitrateVideoCalls ? 1 : 0, forKey: "enableHighBitrateVideoCalls") encoder.encodeInt32(self.playerEmbedding ? 1 : 0, forKey: "playerEmbedding") encoder.encodeInt32(self.playlistPlayback ? 1 : 0, forKey: "playlistPlayback") + if let preferredVideoCodec = self.preferredVideoCodec { + encoder.encodeString(preferredVideoCodec, forKey: "preferredVideoCodec") + } } public func isEqual(to: PreferencesEntry) -> Bool { diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 9b258bf047..3d93fa059a 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -551,7 +551,7 @@ public final class OngoingCallContext { return result } - public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, enableHighBitrateVideoCalls: Bool, audioSessionActive: Signal, logName: String) { + public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, audioSessionActive: Signal, logName: String, preferredVideoCodec: String?) { let _ = setupLogs OngoingCallThreadLocalContext.applyServerConfig(serializedData) @@ -599,7 +599,7 @@ public final class OngoingCallContext { let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in callSessionManager?.sendSignalingData(internalId: internalId, data: data) - }, videoCapturer: video?.impl, preferredAspectRatio: Float(preferredAspectRatio), enableHighBitrateVideoCalls: enableHighBitrateVideoCalls) + }, videoCapturer: video?.impl, preferredAspectRatio: Float(preferredAspectRatio), preferredVideoCodec: preferredVideoCodec) strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in diff --git a/submodules/TgVoipWebrtc/BUCK b/submodules/TgVoipWebrtc/BUCK index 3333a6f854..27191bebbe 100644 --- a/submodules/TgVoipWebrtc/BUCK +++ b/submodules/TgVoipWebrtc/BUCK @@ -14,6 +14,7 @@ static_library( "tgcalls/tgcalls/legacy/**", "tgcalls/tgcalls/platform/tdesktop/**", "tgcalls/tgcalls/platform/windows/**", + "tgcalls/tgcalls/platform/android/**", "tgcalls/tgcalls/platform/darwin/VideoCameraCapturerMac.*", "tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*", ]), diff --git a/submodules/TgVoipWebrtc/BUILD b/submodules/TgVoipWebrtc/BUILD index a71ebfe7a6..edcdb48f07 100644 --- a/submodules/TgVoipWebrtc/BUILD +++ b/submodules/TgVoipWebrtc/BUILD @@ -14,6 +14,7 @@ objc_library( ], exclude = [ "tgcalls/tgcalls/legacy/**", "tgcalls/tgcalls/platform/tdesktop/**", + "tgcalls/tgcalls/platform/android/**", "tgcalls/tgcalls/platform/windows/**", "tgcalls/tgcalls/platform/darwin/VideoCameraCapturerMac.*", "tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*", diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h index 146aa2edac..05e95a3358 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h @@ -123,7 +123,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) { @property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc, OngoingCallVideoStateWebrtc, OngoingCallRemoteVideoStateWebrtc, OngoingCallRemoteAudioStateWebrtc, OngoingCallRemoteBatteryLevelWebrtc, float); @property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t); -- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio enableHighBitrateVideoCalls:(bool)enableHighBitrateVideoCalls; +- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec; - (void)beginTermination; - (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion; diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 8355f98873..b723953ee4 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -299,15 +299,21 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; return 92; } -+ (NSArray * _Nonnull)versionsWithIncludeReference:(bool)includeReference { - if (includeReference) { - return @[@"2.7.7", @"2.8.8"]; ++ (NSArray * _Nonnull)versionsWithIncludeReference:(bool)__unused includeReference { + return @[@"2.7.7", @"3.0.0"]; +} + ++ (tgcalls::ProtocolVersion)protocolVersionFromLibraryVersion:(NSString *)version { + if ([version isEqualToString:@"2.7.7"]) { + return tgcalls::ProtocolVersion::V0; + } else if ([version isEqualToString:@"3.0.0"]) { + return tgcalls::ProtocolVersion::V1; } else { - return @[@"2.7.7"]; + return tgcalls::ProtocolVersion::V0; } } -- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio enableHighBitrateVideoCalls:(bool)enableHighBitrateVideoCalls { +- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec { self = [super init]; if (self != nil) { _version = version; @@ -370,6 +376,11 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } } + std::vector preferredVideoCodecs; + if (preferredVideoCodec != nil) { + preferredVideoCodecs.push_back([preferredVideoCodec UTF8String]); + } + std::vector endpoints; tgcalls::Config config = { @@ -384,7 +395,9 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; .logPath = "", //logPath.length == 0 ? "" : std::string(logPath.UTF8String), .maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer], .preferredAspectRatio = preferredAspectRatio, - .enableHighBitrateVideo = enableHighBitrateVideoCalls + .enableHighBitrateVideo = true, + .preferredVideoCodecs = preferredVideoCodecs, + .protocolVersion = [OngoingCallThreadLocalContextWebrtc protocolVersionFromLibraryVersion:version] }; auto encryptionKeyValue = std::make_shared>(); @@ -396,7 +409,6 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ tgcalls::Register(); - tgcalls::Register(); }); _tgVoip = tgcalls::Meta::Create([version UTF8String], (tgcalls::Descriptor){ .config = config, diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index da1160dfbf..ef7f8b61c7 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit da1160dfbf4ac4b0dee65d481bd9c634932cd5a2 +Subproject commit ef7f8b61c7d02b1a60ee289a436d706b2de1cd49