From ec6a8877f55458613b569c4418f12cb7d1390185 Mon Sep 17 00:00:00 2001
From: Ali <>
Date: Mon, 9 Nov 2020 14:41:27 +0400
Subject: [PATCH] WIP
---
Telegram/BUILD | 16 ++--
build-system/xcode_version | 2 +-
.../Sources/GroupCallController.swift | 45 +++++++++
.../Sources/GroupCallContext.swift | 94 +++++++++++++++++--
.../Sources/OngoingCallContext.swift | 2 +-
.../OngoingCallThreadLocalContext.h | 3 +-
.../Sources/OngoingCallThreadLocalContext.mm | 53 ++++++++++-
7 files changed, 195 insertions(+), 20 deletions(-)
diff --git a/Telegram/BUILD b/Telegram/BUILD
index 6086da8b83..e519409f89 100644
--- a/Telegram/BUILD
+++ b/Telegram/BUILD
@@ -251,9 +251,9 @@ official_bundle_ids = [
"org.telegram.Telegram-iOS",
]
-apple_pay_merchants = []#official_apple_pay_merchants if telegram_bundle_id == "ph.telegra.Telegraph" else ""
+apple_pay_merchants = official_apple_pay_merchants if telegram_bundle_id == "ph.telegra.Telegraph" else []
-apple_pay_merchants_fragment = "" if apple_pay_merchants == "" else """
+apple_pay_merchants_fragment = "" if apple_pay_merchants == [] else """
com.apple.developer.in-app-payments
""" + "\n".join([
@@ -1528,13 +1528,13 @@ ios_application(
":AppStringResources",
],
extensions = [
- #":ShareExtension",
- #":NotificationContentExtension",
- #":NotificationServiceExtension",
- #":IntentsExtension",
- #":WidgetExtension",
+ ":ShareExtension",
+ ":NotificationContentExtension",
+ ":NotificationServiceExtension",
+ ":IntentsExtension",
+ ":WidgetExtension",
],
- #watch_application = ":TelegramWatchApp",
+ watch_application = ":TelegramWatchApp",
deps = [
":Main",
":Lib",
diff --git a/build-system/xcode_version b/build-system/xcode_version
index b700dc1d47..40e6bd96a6 100644
--- a/build-system/xcode_version
+++ b/build-system/xcode_version
@@ -1 +1 @@
-12.0.1
+12.1
diff --git a/submodules/TelegramCallsUI/Sources/GroupCallController.swift b/submodules/TelegramCallsUI/Sources/GroupCallController.swift
index 59d1d7399d..fdf85d8b22 100644
--- a/submodules/TelegramCallsUI/Sources/GroupCallController.swift
+++ b/submodules/TelegramCallsUI/Sources/GroupCallController.swift
@@ -21,6 +21,9 @@ public final class GroupCallController: ViewController {
private var isMutedDisposable: Disposable?
private let audioSessionActive = Promise(false)
+ private var incomingVideoStreamList: [String] = []
+ private var incomingVideoStreamListDisposable: Disposable?
+
private var memberCount: Int = 0
private let memberCountNode: ImmediateTextNode
@@ -28,6 +31,8 @@ public final class GroupCallController: ViewController {
private let isMutedNode: ImmediateTextNode
private let muteButton: HighlightableButtonNode
+ private var videoViews: [OngoingCallContextPresentationCallVideoView] = []
+
private var validLayout: ContainerViewLayout?
init(context: AccountContext) {
@@ -77,6 +82,34 @@ public final class GroupCallController: ViewController {
}
})
+ self.incomingVideoStreamListDisposable = (callContext.videoStreamList
+ |> deliverOnMainQueue).start(next: { [weak self] value in
+ guard let strongSelf = self else {
+ return
+ }
+ var addedStreamIds: [String] = []
+ for id in value {
+ if !strongSelf.incomingVideoStreamList.contains(id) {
+ addedStreamIds.append(id)
+ }
+ }
+ strongSelf.incomingVideoStreamList = value
+
+ for id in addedStreamIds {
+ callContext.makeIncomingVideoView(id: id, completion: { videoView in
+ guard let strongSelf = self, let videoView = videoView else {
+ return
+ }
+ strongSelf.videoViews.append(videoView)
+ videoView.view.backgroundColor = .black
+ strongSelf.view.addSubview(videoView.view)
+ 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 {
@@ -94,6 +127,7 @@ public final class GroupCallController: ViewController {
deinit {
self.callDisposable?.dispose()
self.memberCountDisposable?.dispose()
+ self.incomingVideoStreamListDisposable?.dispose()
}
@objc private func muteButtonPressed() {
@@ -116,6 +150,17 @@ public final class GroupCallController: ViewController {
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)
+
+ let videoSize = CGSize(width: 200.0, height: 360.0)
+ var nextVideoOrigin = CGPoint()
+ for videoView in self.videoViews {
+ videoView.view.frame = CGRect(origin: nextVideoOrigin, size: videoSize)
+ nextVideoOrigin.x += videoSize.width
+ if nextVideoOrigin.x + videoSize.width > layout.size.width {
+ nextVideoOrigin.x = 0.0
+ nextVideoOrigin.y += videoSize.height
+ }
+ }
}
}
diff --git a/submodules/TelegramVoip/Sources/GroupCallContext.swift b/submodules/TelegramVoip/Sources/GroupCallContext.swift
index ed915a8cc6..9ce189cfcc 100644
--- a/submodules/TelegramVoip/Sources/GroupCallContext.swift
+++ b/submodules/TelegramVoip/Sources/GroupCallContext.swift
@@ -501,9 +501,15 @@ private extension ConferenceDescription.Content.Channel.PayloadType {
[
"type": "transport-cc"
] as [String: Any],
- /*[
+ [
+ "type": "ccm fir"
+ ] as [String: Any],
+ [
"type": "nack"
- ] as [String: Any]*/
+ ] as [String: Any],
+ [
+ "type": "nack pli"
+ ] as [String: Any],
] as [Any]
if let parameters = self.parameters {
result["parameters"] = parameters
@@ -782,7 +788,7 @@ private extension ConferenceDescription {
appendSdp("a=ssrc:\(stream.audioSsrc) label:audio\(stream.audioSsrc)")
}
- appendSdp("m=video \(stream.isMain ? "1" : "0") RTP/SAVPF 100")
+ appendSdp("m=video 0 RTP/SAVPF 100")
appendSdp("a=mid:video\(stream.videoSsrc)")
if stream.isRemoved {
appendSdp("a=inactive")
@@ -809,6 +815,11 @@ private extension ConferenceDescription {
appendSdp("a=rtcp-fb:100 nack")
appendSdp("a=rtcp-fb:100 nack pli")
+ if stream.isMain {
+ appendSdp("a=sendrecv")
+ } else {
+ appendSdp("a=sendonly")
+ }
appendSdp("a=bundle-only")
appendSdp("a=ssrc-group:FID \(stream.videoSsrc)")
@@ -1026,9 +1037,6 @@ private extension ConferenceDescription {
[
"type": "transport-cc"
] as [String: Any],
- /*[
- "type": "nack"
- ] as [String: Any]*/
] as [Any]
]
),
@@ -1075,9 +1083,15 @@ private extension ConferenceDescription {
[
"type": "transport-cc"
] as [String: Any],
+ [
+ "type": "ccm fir"
+ ] as [String: Any],
[
"type": "nack"
- ] as [String: Any]
+ ] as [String: Any],
+ [
+ "type": "nack pli"
+ ] as [String: Any],
] as [Any]
]
)
@@ -1263,6 +1277,7 @@ public final class GroupCallContext {
private var localTransport: ConferenceDescription.Transport?
let memberCount = ValuePromise(0, ignoreRepeated: true)
+ let videoStreamList = ValuePromise<[String]>([], ignoreRepeated: true)
private var isMutedValue: Bool = false
let isMuted = ValuePromise(false, ignoreRepeated: true)
@@ -1271,14 +1286,20 @@ public final class GroupCallContext {
self.queue = queue
self.sessionId = UInt32.random(in: 0 ..< UInt32(Int32.max))
- self.colibriHost = "192.168.8.118"
+ self.colibriHost = "192.168.93.24"
var relaySdpAnswerImpl: ((String) -> Void)?
+ let videoStreamList = self.videoStreamList
+
self.context = GroupCallThreadLocalContext(queue: ContextQueueImpl(queue: queue), relaySdpAnswer: { sdpAnswer in
queue.async {
relaySdpAnswerImpl?(sdpAnswer)
}
+ }, incomingVideoStreamListUpdated: { streamList in
+ queue.async {
+ videoStreamList.set(streamList)
+ }
}, videoCapturer: video?.impl)
relaySdpAnswerImpl = { [weak self] sdpAnswer in
@@ -1595,6 +1616,45 @@ public final class GroupCallContext {
self.isMuted.set(self.isMutedValue)
self.context.setIsMuted(self.isMutedValue)
}
+
+ func makeIncomingVideoView(id: String, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
+ self.context.makeIncomingVideoView(withStreamId: id, completion: { 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)
+ }
+ }
+ ))
+ } else {
+ completion(nil)
+ }
+ })
+ }
}
private let queue = Queue()
@@ -1619,6 +1679,18 @@ public final class GroupCallContext {
}
}
+ public var videoStreamList: Signal<[String], NoError> {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+ self.impl.with { impl in
+ disposable.set(impl.videoStreamList.get().start(next: { value in
+ subscriber.putNext(value)
+ }))
+ }
+ return disposable
+ }
+ }
+
public var isMuted: Signal {
return Signal { subscriber in
let disposable = MetaDisposable()
@@ -1636,5 +1708,11 @@ public final class GroupCallContext {
impl.toggleIsMuted()
}
}
+
+ public func makeIncomingVideoView(id: String, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
+ self.impl.with { impl in
+ impl.makeIncomingVideoView(id: id, completion: completion)
+ }
+ }
}
diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift
index 564317977c..606beeb2d9 100644
--- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift
+++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift
@@ -472,7 +472,7 @@ public enum OngoingCallVideoOrientation {
case rotation270
}
-private extension OngoingCallVideoOrientation {
+extension OngoingCallVideoOrientation {
init(_ orientation: OngoingCallVideoOrientationWebrtc) {
switch orientation {
case .orientation0:
diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h
index 5462ff7bc2..dd5ea3b001 100644
--- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h
+++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h
@@ -154,11 +154,12 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
@interface GroupCallThreadLocalContext : NSObject
-- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
+- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer incomingVideoStreamListUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoStreamListUpdated videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
- (void)emitOffer;
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial;
- (void)setIsMuted:(bool)isMuted;
+- (void)makeIncomingVideoViewWithStreamId:(NSString * _Nonnull)streamId completion:(void (^_Nonnull)(UIView * _Nullable))completion;
@end
diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm
index f8c96b3322..55a55c498f 100644
--- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm
+++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm
@@ -806,7 +806,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
@implementation GroupCallThreadLocalContext
-- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
+- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer incomingVideoStreamListUpdated:(void (^ _Nonnull)(NSArray * _Nonnull))incomingVideoStreamListUpdated videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
self = [super init];
if (self != nil) {
_queue = queue;
@@ -825,6 +825,19 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
relaySdpAnswer(string);
}];
},
+ .incomingVideoStreamListUpdated = [weakSelf, queue, incomingVideoStreamListUpdated](std::vector const &incomingVideoStreamList) {
+ NSMutableArray *mappedList = [[NSMutableArray alloc] init];
+ for (auto &it : incomingVideoStreamList) {
+ [mappedList addObject:[NSString stringWithUTF8String:it.c_str()]];
+ }
+ [queue dispatch:^{
+ __strong GroupCallThreadLocalContext *strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
+ incomingVideoStreamListUpdated(mappedList);
+ }];
+ },
.videoCapture = [_videoCapturer getInterface]
}));
}
@@ -849,5 +862,43 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
}
+- (void)makeIncomingVideoViewWithStreamId:(NSString * _Nonnull)streamId completion:(void (^_Nonnull)(UIView * _Nullable))completion {
+ if (_instance) {
+ __weak GroupCallThreadLocalContext *weakSelf = self;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if ([VideoMetalView isSupported]) {
+ VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
+#if TARGET_OS_IPHONE
+ remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
+#else
+ remoteRenderer.videoContentMode = UIViewContentModeScaleAspect;
+#endif
+
+ std::shared_ptr> sink = [remoteRenderer getSink];
+ __strong GroupCallThreadLocalContext *strongSelf = weakSelf;
+ if (strongSelf) {
+ //[remoteRenderer setOrientation:strongSelf->_remoteVideoOrientation];
+ //strongSelf->_currentRemoteVideoRenderer = remoteRenderer;
+ strongSelf->_instance->setIncomingVideoOutput([streamId UTF8String], sink);
+ }
+
+ completion(remoteRenderer);
+ } else {
+ GLVideoView *remoteRenderer = [[GLVideoView alloc] initWithFrame:CGRectZero];
+
+ std::shared_ptr> sink = [remoteRenderer getSink];
+ __strong GroupCallThreadLocalContext *strongSelf = weakSelf;
+ if (strongSelf) {
+ //[remoteRenderer setOrientation:strongSelf->_remoteVideoOrientation];
+ //strongSelf->_currentRemoteVideoRenderer = remoteRenderer;
+ strongSelf->_instance->setIncomingVideoOutput([streamId UTF8String], sink);
+ }
+
+ completion(remoteRenderer);
+ }
+ });
+ }
+}
+
@end