Merge branch 'experiments/group-calls' into experimental-2

This commit is contained in:
Ali 2020-10-16 19:34:11 +04:00
commit 1626214f66
11 changed files with 1716 additions and 6 deletions

View File

@ -13,6 +13,7 @@ import ItemListUI
import PresentationDataUtils
import OverlayStatusController
import AccountContext
import TelegramCallsUI
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
@ -74,6 +75,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case alternativeFolderTabs(Bool)
case playerEmbedding(Bool)
case playlistPlayback(Bool)
case voiceConference
case preferredVideoCodec(Int, String, String?, Bool)
case disableVideoAspectScaling(Bool)
case enableVoipTcp(Bool)
@ -90,7 +92,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, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback:
case .clearTips, .reimport, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback, .voiceConference:
return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue
@ -155,8 +157,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 24
case .playlistPlayback:
return 25
case .voiceConference:
return 26
case let .preferredVideoCodec(index, _, _, _):
return 26 + index
return 27 + index
case .disableVideoAspectScaling:
return 100
case .enableVoipTcp:
@ -648,6 +652,15 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
}).start()
})
case .voiceConference:
return ItemListDisclosureItem(presentationData: presentationData, title: "Voice Conference (Test)", label: "", sectionId: self.section, style: .blocks, action: {
guard let context = arguments.context else {
return
}
let controller = GroupCallController(context: context)
controller.navigationPresentation = .modal
arguments.pushController(controller)
})
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
@ -725,6 +738,8 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
entries.append(.voiceConference)
let codecs: [(String, String?)] = [
("No Preference", nil),
("H265", "H265"),

View File

@ -0,0 +1,147 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import TelegramVoip
import TelegramAudio
import AccountContext
public final class GroupCallController: ViewController {
private final class Node: ViewControllerTracingNode {
private let context: AccountContext
private let presentationData: PresentationData
private var callContext: GroupCallContext?
private var callDisposable: Disposable?
private var memberCountDisposable: Disposable?
private var isMutedDisposable: Disposable?
private let audioSessionActive = Promise<Bool>(false)
private var memberCount: Int = 0
private let memberCountNode: ImmediateTextNode
private var isMuted: Bool = false
private let isMutedNode: ImmediateTextNode
private let muteButton: HighlightableButtonNode
private var validLayout: ContainerViewLayout?
init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.memberCountNode = ImmediateTextNode()
self.isMutedNode = ImmediateTextNode()
self.muteButton = HighlightableButtonNode()
super.init()
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.memberCountNode)
self.muteButton.addSubnode(self.isMutedNode)
self.addSubnode(self.muteButton)
let audioSessionActive = self.audioSessionActive
self.callDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .voiceCall, manualActivate: { audioSessionControl in
audioSessionControl.activate({ _ in })
audioSessionActive.set(.single(true))
}, deactivate: {
return Signal { subscriber in
subscriber.putCompletion()
return EmptyDisposable
}
}, availableOutputsChanged: { _, _ in
})
let callContext = GroupCallContext(audioSessionActive: self.audioSessionActive.get())
self.callContext = callContext
self.memberCountDisposable = (callContext.memberCount
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.memberCount = value
if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
}
})
self.isMutedDisposable = (callContext.isMuted
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.isMuted = value
if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
}
})
self.muteButton.addTarget(self, action: #selector(self.muteButtonPressed), forControlEvents: .touchUpInside)
}
deinit {
self.callDisposable?.dispose()
self.memberCountDisposable?.dispose()
}
@objc private func muteButtonPressed() {
self.callContext?.toggleIsMuted()
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
self.memberCountNode.attributedText = NSAttributedString(string: "Members: \(self.memberCount)", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
self.isMutedNode.attributedText = NSAttributedString(string: self.isMuted ? "Unmute" : "Mute", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemAccentColor)
let textSize = self.memberCountNode.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: 100.0))
let isMutedSize = self.isMutedNode.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: 100.0))
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: floor((layout.size.height - textSize.width) / 2.0)), size: textSize)
transition.updateFrameAdditiveToCenter(node: self.memberCountNode, frame: textFrame)
let isMutedFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - isMutedSize.width) / 2.0), y: textFrame.maxY + 12.0), size: isMutedSize)
transition.updateFrame(node: self.muteButton, frame: isMutedFrame)
self.isMutedNode.frame = CGRect(origin: CGPoint(), size: isMutedFrame.size)
}
}
private let context: AccountContext
private let presentationData: PresentationData
private var controllerNode: Node {
return self.displayNode as! Node
}
public init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func loadDisplayNode() {
self.displayNode = Node(context: self.context)
self.displayNodeDidLoad()
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
}
}

View File

@ -52,6 +52,7 @@ import PeerInfoUI
import ListMessageItem
import GalleryData
import ChatInterfaceState
import TelegramVoip
protocol PeerInfoScreenItem: class {
var id: AnyHashable { get }
@ -3153,6 +3154,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.controller?.present(shareController, in: .window(.root))
}
private let groupCallDisposable = MetaDisposable()
private var groupCall: GroupCallContext?
private func requestCall(isVideo: Bool) {
guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else {
return

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
#ifndef GroupCallThreadLocalContext_h
#define GroupCallThreadLocalContext_h
#import <Foundation/Foundation.h>
#import <TgVoipWebrtc/OngoingCallThreadLocalContext.h>
@interface GroupCallThreadLocalContext : NSObject
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer;
- (void)emitOffer;
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial;
- (void)setIsMuted:(bool)isMuted;
@end
#endif

View File

@ -0,0 +1,57 @@
#import <TgVoipWebrtc/GroupCallThreadLocalContext.h>
#import "group/GroupInstanceImpl.h"
@interface GroupCallThreadLocalContext () {
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;
}
@end
@implementation GroupCallThreadLocalContext
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer {
self = [super init];
if (self != nil) {
_queue = queue;
tgcalls::GroupInstanceDescriptor descriptor;
__weak GroupCallThreadLocalContext *weakSelf = self;
descriptor.sdpAnswerEmitted = [weakSelf, queue, relaySdpAnswer](std::string const &sdpAnswer) {
NSString *string = [NSString stringWithUTF8String:sdpAnswer.c_str()];
[queue dispatch:^{
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
relaySdpAnswer(string);
}];
};
_instance.reset(new tgcalls::GroupInstanceImpl(std::move(descriptor)));
}
return self;
}
- (void)emitOffer {
if (_instance) {
_instance->emitOffer();
}
}
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial {
if (_instance) {
_instance->setOfferSdp([offerSdp UTF8String], isPartial);
}
}
- (void)setIsMuted:(bool)isMuted {
if (_instance) {
_instance->setIsMuted(isMuted);
}
}
@end

View File

@ -1,7 +1,7 @@
#ifndef WEBRTC_IOS
#import "OngoingCallThreadLocalContext.h"
#else
#import <TgVoip/OngoingCallThreadLocalContext.h>
#import <TgVoipWebrtc/OngoingCallThreadLocalContext.h>
#endif

@ -1 +1 @@
Subproject commit 64f96a1b4fcfb8afdb0fb7749082cb42cdad7901
Subproject commit 4f7501d281b851e6302b2a2d7298c733eee82414

View File

@ -1,4 +1,4 @@
use_gn_build = True
use_gn_build = False
webrtc_libs = [
"libwebrtc.a",

@ -1 +1 @@
Subproject commit 11255bcfff3180210a012f368e2d2bcd169b6877
Subproject commit 782743c7931d09c1d2e4a0cf6cd349ee45452f1d