Refactor CallVC

pull/560/head
Niels Andriesse 4 years ago
parent 4dd218daf6
commit 662fc945e2

@ -201,6 +201,10 @@
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
B875885A264503A6000E60D0 /* JoinOpenGroupModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */; }; B875885A264503A6000E60D0 /* JoinOpenGroupModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */; };
B877E24226CA12910007970A /* CallVCV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B877E24126CA12910007970A /* CallVCV2.swift */; };
B877E24426CA12F00007970A /* CallVCV2+MessageSending.swift in Sources */ = {isa = PBXBuildFile; fileRef = B877E24326CA12F00007970A /* CallVCV2+MessageSending.swift */; };
B877E24626CA13BA0007970A /* CallVCV2+Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = B877E24526CA13BA0007970A /* CallVCV2+Camera.swift */; };
B877E24826CA15170007970A /* CallVCV2+WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = B877E24726CA15170007970A /* CallVCV2+WebSocket.swift */; };
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; }; B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; };
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B879D448247E1BE300DB3608 /* PathVC.swift */; }; B879D449247E1BE300DB3608 /* PathVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B879D448247E1BE300DB3608 /* PathVC.swift */; };
B87EF17126367CF800124B3C /* FileServerAPIV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B87EF17026367CF800124B3C /* FileServerAPIV2.swift */; }; B87EF17126367CF800124B3C /* FileServerAPIV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B87EF17026367CF800124B3C /* FileServerAPIV2.swift */; };
@ -253,7 +257,7 @@
B8B558F926C4CE6800693325 /* CallVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558F826C4CE6800693325 /* CallVC.swift */; }; B8B558F926C4CE6800693325 /* CallVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558F826C4CE6800693325 /* CallVC.swift */; };
B8B558FB26C4D25C00693325 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558FA26C4D25C00693325 /* WebSocket.swift */; }; B8B558FB26C4D25C00693325 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558FA26C4D25C00693325 /* WebSocket.swift */; };
B8B558FD26C4D35400693325 /* TestCallServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558FC26C4D35400693325 /* TestCallServer.swift */; }; B8B558FD26C4D35400693325 /* TestCallServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558FC26C4D35400693325 /* TestCallServer.swift */; };
B8B558FF26C4E05E00693325 /* CallManager+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558FE26C4E05E00693325 /* CallManager+Messages.swift */; }; B8B558FF26C4E05E00693325 /* CallManager+MessageHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B558FE26C4E05E00693325 /* CallManager+MessageHandling.swift */; };
B8B5590126C4E2A400693325 /* SignalingMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B5590026C4E2A400693325 /* SignalingMessage.swift */; }; B8B5590126C4E2A400693325 /* SignalingMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B5590026C4E2A400693325 /* SignalingMessage.swift */; };
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82A4238F627000BA5194 /* HomeVC.swift */; }; B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82A4238F627000BA5194 /* HomeVC.swift */; };
B8BC00C0257D90E30032E807 /* General.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BC00BF257D90E30032E807 /* General.swift */; }; B8BC00C0257D90E30032E807 /* General.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BC00BF257D90E30032E807 /* General.swift */; };
@ -1200,6 +1204,10 @@
B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = "<group>"; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = "<group>"; };
B87588582644CA9D000E60D0 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; }; B87588582644CA9D000E60D0 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinOpenGroupModal.swift; sourceTree = "<group>"; }; B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinOpenGroupModal.swift; sourceTree = "<group>"; };
B877E24126CA12910007970A /* CallVCV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallVCV2.swift; sourceTree = "<group>"; };
B877E24326CA12F00007970A /* CallVCV2+MessageSending.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CallVCV2+MessageSending.swift"; sourceTree = "<group>"; };
B877E24526CA13BA0007970A /* CallVCV2+Camera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CallVCV2+Camera.swift"; sourceTree = "<group>"; };
B877E24726CA15170007970A /* CallVCV2+WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CallVCV2+WebSocket.swift"; sourceTree = "<group>"; };
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = "<group>"; }; B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = "<group>"; };
B879D448247E1BE300DB3608 /* PathVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathVC.swift; sourceTree = "<group>"; }; B879D448247E1BE300DB3608 /* PathVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathVC.swift; sourceTree = "<group>"; };
B879D44A247E1D9200DB3608 /* PathStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathStatusView.swift; sourceTree = "<group>"; }; B879D44A247E1D9200DB3608 /* PathStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathStatusView.swift; sourceTree = "<group>"; };
@ -1229,7 +1237,7 @@
B8B558F826C4CE6800693325 /* CallVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallVC.swift; sourceTree = "<group>"; }; B8B558F826C4CE6800693325 /* CallVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallVC.swift; sourceTree = "<group>"; };
B8B558FA26C4D25C00693325 /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = "<group>"; }; B8B558FA26C4D25C00693325 /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = "<group>"; };
B8B558FC26C4D35400693325 /* TestCallServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCallServer.swift; sourceTree = "<group>"; }; B8B558FC26C4D35400693325 /* TestCallServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCallServer.swift; sourceTree = "<group>"; };
B8B558FE26C4E05E00693325 /* CallManager+Messages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CallManager+Messages.swift"; sourceTree = "<group>"; }; B8B558FE26C4E05E00693325 /* CallManager+MessageHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CallManager+MessageHandling.swift"; sourceTree = "<group>"; };
B8B5590026C4E2A400693325 /* SignalingMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalingMessage.swift; sourceTree = "<group>"; }; B8B5590026C4E2A400693325 /* SignalingMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalingMessage.swift; sourceTree = "<group>"; };
B8B5BCEB2394D869003823C9 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; }; B8B5BCEB2394D869003823C9 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
B8BAC75B2695645400EA1759 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = "<group>"; }; B8BAC75B2695645400EA1759 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -2190,6 +2198,18 @@
path = "Message Cells"; path = "Message Cells";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
B877E24026CA11170007970A /* Temp */ = {
isa = PBXGroup;
children = (
B8B5590026C4E2A400693325 /* SignalingMessage.swift */,
B8B558FA26C4D25C00693325 /* WebSocket.swift */,
B8B558F226C4CA4600693325 /* TestCallConfig.swift */,
B8B558FC26C4D35400693325 /* TestCallServer.swift */,
B80F469926C63DD000DCE243 /* RoomInfo.swift */,
);
path = Temp;
sourceTree = "<group>";
};
B887C38125C7C79700E11DAE /* Input View */ = { B887C38125C7C79700E11DAE /* Input View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2331,6 +2351,10 @@
B8B558ED26C4B55F00693325 /* Calls */ = { B8B558ED26C4B55F00693325 /* Calls */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B877E24126CA12910007970A /* CallVCV2.swift */,
B877E24326CA12F00007970A /* CallVCV2+MessageSending.swift */,
B877E24526CA13BA0007970A /* CallVCV2+Camera.swift */,
B877E24726CA15170007970A /* CallVCV2+WebSocket.swift */,
B8B558F826C4CE6800693325 /* CallVC.swift */, B8B558F826C4CE6800693325 /* CallVC.swift */,
B8B558EE26C4B56C00693325 /* VideoCallVC.swift */, B8B558EE26C4B56C00693325 /* VideoCallVC.swift */,
B8B558F026C4BB0600693325 /* CameraManager.swift */, B8B558F026C4BB0600693325 /* CameraManager.swift */,
@ -2367,14 +2391,10 @@
B8DE1FB226C22F1F0079C9CE /* Calls */ = { B8DE1FB226C22F1F0079C9CE /* Calls */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B877E24026CA11170007970A /* Temp */,
B8DE1FB326C22F2F0079C9CE /* CallManager.swift */, B8DE1FB326C22F2F0079C9CE /* CallManager.swift */,
B806ECA026C4A7E4008BDA44 /* CallManager+UI.swift */, B806ECA026C4A7E4008BDA44 /* CallManager+UI.swift */,
B8B558FE26C4E05E00693325 /* CallManager+Messages.swift */, B8B558FE26C4E05E00693325 /* CallManager+MessageHandling.swift */,
B8B5590026C4E2A400693325 /* SignalingMessage.swift */,
B8B558FA26C4D25C00693325 /* WebSocket.swift */,
B8B558F226C4CA4600693325 /* TestCallConfig.swift */,
B8B558FC26C4D35400693325 /* TestCallServer.swift */,
B80F469926C63DD000DCE243 /* RoomInfo.swift */,
); );
path = Calls; path = Calls;
sourceTree = "<group>"; sourceTree = "<group>";
@ -4688,7 +4708,7 @@
C352A32F2557549C00338F3E /* NotifyPNServerJob.swift in Sources */, C352A32F2557549C00338F3E /* NotifyPNServerJob.swift in Sources */,
7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */, 7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */,
C300A5F22554B09800555489 /* MessageSender.swift in Sources */, C300A5F22554B09800555489 /* MessageSender.swift in Sources */,
B8B558FF26C4E05E00693325 /* CallManager+Messages.swift in Sources */, B8B558FF26C4E05E00693325 /* CallManager+MessageHandling.swift in Sources */,
C3C2A74D2553A39700C340D1 /* VisibleMessage.swift in Sources */, C3C2A74D2553A39700C340D1 /* VisibleMessage.swift in Sources */,
C32C5AAD256DBE8F003C73A2 /* TSInfoMessage.m in Sources */, C32C5AAD256DBE8F003C73A2 /* TSInfoMessage.m in Sources */,
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */, C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
@ -4842,6 +4862,7 @@
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */, B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */,
B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */, B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */,
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */, B879D449247E1BE300DB3608 /* PathVC.swift in Sources */,
B877E24626CA13BA0007970A /* CallVCV2+Camera.swift in Sources */,
454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */, 454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */,
340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */, 340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */,
34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */, 34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */,
@ -4904,6 +4925,7 @@
B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */, B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */,
B8AF4BB426A5204600583500 /* SendSeedModal.swift in Sources */, B8AF4BB426A5204600583500 /* SendSeedModal.swift in Sources */,
B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */, B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */,
B877E24226CA12910007970A /* CallVCV2.swift in Sources */,
C374EEEB25DA3CA70073A857 /* ConversationTitleView.swift in Sources */, C374EEEB25DA3CA70073A857 /* ConversationTitleView.swift in Sources */,
B88FA7F2260C3EB10049422F /* OpenGroupSuggestionGrid.swift in Sources */, B88FA7F2260C3EB10049422F /* OpenGroupSuggestionGrid.swift in Sources */,
4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */, 4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */,
@ -4943,6 +4965,7 @@
B8041A9525C8FA1D003C2166 /* MediaLoaderView.swift in Sources */, B8041A9525C8FA1D003C2166 /* MediaLoaderView.swift in Sources */,
45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */, 45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */,
34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */, 34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */,
B877E24826CA15170007970A /* CallVCV2+WebSocket.swift in Sources */,
4CA46F4C219CCC630038ABDE /* CaptionView.swift in Sources */, 4CA46F4C219CCC630038ABDE /* CaptionView.swift in Sources */,
C328253025CA55370062D0A7 /* ContextMenuWindow.swift in Sources */, C328253025CA55370062D0A7 /* ContextMenuWindow.swift in Sources */,
340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */, 340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */,
@ -4952,6 +4975,7 @@
C328250F25CA06020062D0A7 /* VoiceMessageView.swift in Sources */, C328250F25CA06020062D0A7 /* VoiceMessageView.swift in Sources */,
B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */, B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */,
3488F9362191CC4000E524CC /* MediaView.swift in Sources */, 3488F9362191CC4000E524CC /* MediaView.swift in Sources */,
B877E24426CA12F00007970A /* CallVCV2+MessageSending.swift in Sources */,
B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */, B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */,
3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */, 3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */,
C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */, C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */,

@ -1,215 +1,215 @@
import UIKit //import UIKit
import AVFoundation //import AVFoundation
import WebRTC //import WebRTC
//
final class CallVC : UIViewController, CameraCaptureDelegate, CallManagerDelegate, WebSocketDelegate { //final class CallVC : UIViewController, CameraCaptureDelegate, CallManagerDelegate, WebSocketDelegate {
private var webSocket: WebSocket? // private var webSocket: WebSocket?
private let videoCallVC = VideoCallVC() // private let videoCallVC = VideoCallVC()
let videoCapturer: RTCVideoCapturer = RTCCameraVideoCapturer(delegate: CallManager.shared.localVideoSource) // let videoCapturer: RTCVideoCapturer = RTCCameraVideoCapturer(delegate: CallManager.shared.localVideoSource)
private var messageQueue: [String] = [] // private var messageQueue: [String] = []
private var isConnected = false { // private var isConnected = false {
didSet { // didSet {
let title = isConnected ? "Leave" : "Join" // let title = isConnected ? "Leave" : "Join"
joinOrLeaveButton.setTitle(title, for: UIControl.State.normal) // joinOrLeaveButton.setTitle(title, for: UIControl.State.normal)
} // }
} // }
private var currentRoomInfo: RoomInfo? // private var currentRoomInfo: RoomInfo?
//
var isInitiator: Bool { // var isInitiator: Bool {
return currentRoomInfo?.isInitiator == true // return currentRoomInfo?.isInitiator == true
} // }
//
// MARK: UI Components // // MARK: UI Components
private lazy var previewView: UIImageView = { // private lazy var previewView: UIImageView = {
return UIImageView() // return UIImageView()
}() // }()
//
private lazy var containerView: UIView = { // private lazy var containerView: UIView = {
return UIView() // return UIView()
}() // }()
//
private lazy var joinOrLeaveButton: UIButton = { // private lazy var joinOrLeaveButton: UIButton = {
let result = UIButton() // let result = UIButton()
result.setTitle("Join", for: UIControl.State.normal) // result.setTitle("Join", for: UIControl.State.normal)
result.addTarget(self, action: #selector(joinOrLeave), for: UIControl.Event.touchUpInside) // result.addTarget(self, action: #selector(joinOrLeave), for: UIControl.Event.touchUpInside)
return result // return result
}() // }()
//
private lazy var roomNumberTextField: UITextField = { // private lazy var roomNumberTextField: UITextField = {
let result = UITextField() // let result = UITextField()
result.set(.width, to: 120) // result.set(.width, to: 120)
result.set(.height, to: 40) // result.set(.height, to: 40)
result.backgroundColor = UIColor.white.withAlphaComponent(0.1) // result.backgroundColor = UIColor.white.withAlphaComponent(0.1)
return result // return result
}() // }()
//
private lazy var infoTextView: UITextView = { // private lazy var infoTextView: UITextView = {
let result = UITextView() // let result = UITextView()
result.backgroundColor = UIColor.white.withAlphaComponent(0.1) // result.backgroundColor = UIColor.white.withAlphaComponent(0.1)
return result // return result
}() // }()
//
// MARK: Lifecycle // // MARK: Lifecycle
override func viewDidLoad() { // override func viewDidLoad() {
super.viewDidLoad() // super.viewDidLoad()
touch(CallManager.shared) // touch(CallManager.shared)
CallManager.shared.delegate = self // CallManager.shared.delegate = self
CameraManager.shared.delegate = self // CameraManager.shared.delegate = self
CameraManager.shared.prepare() // CameraManager.shared.prepare()
addChild(videoCallVC) // addChild(videoCallVC)
containerView.addSubview(videoCallVC.view) // containerView.addSubview(videoCallVC.view)
videoCallVC.view.translatesAutoresizingMaskIntoConstraints = false // videoCallVC.view.translatesAutoresizingMaskIntoConstraints = false
videoCallVC.view.pin(to: containerView) // videoCallVC.view.pin(to: containerView)
view.addSubview(containerView) // view.addSubview(containerView)
containerView.pin(to: view) // containerView.pin(to: view)
view.addSubview(joinOrLeaveButton) // view.addSubview(joinOrLeaveButton)
joinOrLeaveButton.translatesAutoresizingMaskIntoConstraints = false // joinOrLeaveButton.translatesAutoresizingMaskIntoConstraints = false
joinOrLeaveButton.pin([ UIView.VerticalEdge.top, UIView.HorizontalEdge.right ], to: view) // joinOrLeaveButton.pin([ UIView.VerticalEdge.top, UIView.HorizontalEdge.right ], to: view)
view.addSubview(roomNumberTextField) // view.addSubview(roomNumberTextField)
roomNumberTextField.translatesAutoresizingMaskIntoConstraints = false // roomNumberTextField.translatesAutoresizingMaskIntoConstraints = false
roomNumberTextField.pin([ UIView.VerticalEdge.top, UIView.HorizontalEdge.left ], to: view) // roomNumberTextField.pin([ UIView.VerticalEdge.top, UIView.HorizontalEdge.left ], to: view)
view.addSubview(infoTextView) // view.addSubview(infoTextView)
infoTextView.translatesAutoresizingMaskIntoConstraints = false // infoTextView.translatesAutoresizingMaskIntoConstraints = false
infoTextView.pin([ UIView.HorizontalEdge.left, UIView.HorizontalEdge.right ], to: view) // infoTextView.pin([ UIView.HorizontalEdge.left, UIView.HorizontalEdge.right ], to: view)
infoTextView.center(.vertical, in: view) // infoTextView.center(.vertical, in: view)
infoTextView.set(.height, to: 200) // infoTextView.set(.height, to: 200)
} // }
//
override func viewDidAppear(_ animated: Bool) { // override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) // super.viewDidAppear(animated)
CameraManager.shared.start() // CameraManager.shared.start()
} // }
//
override func viewWillDisappear(_ animated: Bool) { // override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) // super.viewWillDisappear(animated)
CameraManager.shared.stop() // CameraManager.shared.stop()
} // }
//
// MARK: Interaction // // MARK: Interaction
@objc private func joinOrLeave() { // @objc private func joinOrLeave() {
guard let roomID = roomNumberTextField.text, !roomID.isEmpty else { return } // guard let roomID = roomNumberTextField.text, !roomID.isEmpty else { return }
if isConnected { // if isConnected {
disconnect() // disconnect()
} else { // } else {
isConnected = true // isConnected = true
TestCallServer.join(roomID: roomID).done2 { [weak self] info in // TestCallServer.join(roomID: roomID).done2 { [weak self] info in
guard let self = self else { return } // guard let self = self else { return }
self.log("Successfully joined room.") // self.log("Successfully joined room.")
self.currentRoomInfo = info // self.currentRoomInfo = info
if let messages = info.messages { // if let messages = info.messages {
self.handle(messages) // self.handle(messages)
} // }
let webSocket = WebSocket(url: URL(string: info.wssURL)!) // let webSocket = WebSocket(url: URL(string: info.wssURL)!)
webSocket.delegate = self // webSocket.delegate = self
self.webSocket = webSocket // self.webSocket = webSocket
}.catch2 { [weak self] error in // }.catch2 { [weak self] error in
guard let self = self else { return } // guard let self = self else { return }
self.isConnected = false // self.isConnected = false
self.log("Couldn't join room due to error: \(error).") // self.log("Couldn't join room due to error: \(error).")
SNLog("Couldn't join room due to error: \(error).") // SNLog("Couldn't join room due to error: \(error).")
} // }
roomNumberTextField.resignFirstResponder() // roomNumberTextField.resignFirstResponder()
} // }
} // }
//
private func disconnect() { // private func disconnect() {
guard let info = currentRoomInfo else { return } // guard let info = currentRoomInfo else { return }
TestCallServer.leave(roomID: info.roomID, userID: info.clientID).done2 { [weak self] in // TestCallServer.leave(roomID: info.roomID, userID: info.clientID).done2 { [weak self] in
guard let self = self else { return } // guard let self = self else { return }
self.log("Disconnected.") // self.log("Disconnected.")
} // }
let message = [ "type": "bye" ] // let message = [ "type": "bye" ]
guard let data = try? JSONSerialization.data(withJSONObject: message, options: [.prettyPrinted]) else { return } // guard let data = try? JSONSerialization.data(withJSONObject: message, options: [.prettyPrinted]) else { return }
webSocket?.send(data) // webSocket?.send(data)
webSocket?.delegate = nil // webSocket?.delegate = nil
webSocket = nil // webSocket = nil
currentRoomInfo = nil // currentRoomInfo = nil
isConnected = false // isConnected = false
CallManager.shared.endCall() // CallManager.shared.endCall()
} // }
//
// MARK: Message Handling // // MARK: Message Handling
func handle(_ messages: [String]) { // func handle(_ messages: [String]) {
messageQueue.append(contentsOf: messages) // messageQueue.append(contentsOf: messages)
drainMessageQueue() // drainMessageQueue()
} // }
//
func drainMessageQueue() { // func drainMessageQueue() {
guard isConnected else { return } // guard isConnected else { return }
for message in messageQueue { // for message in messageQueue {
handle(message) // handle(message)
} // }
messageQueue.removeAll() // messageQueue.removeAll()
CallManager.shared.drainMessageQueue() // CallManager.shared.drainICECandidateQueue()
} // }
//
func handle(_ message: String) { // func handle(_ message: String) {
let signalingMessage = SignalingMessage.from(message: message) // let signalingMessage = SignalingMessage.from(message: message)
switch signalingMessage { // switch signalingMessage {
case .candidate(let candidate): // case .candidate(let candidate):
CallManager.shared.handleCandidateMessage(candidate) // CallManager.shared.handleCandidateMessage(candidate)
log("Candidate received.") // log("Candidate received.")
case .answer(let answer): // case .answer(let answer):
CallManager.shared.handleRemoteDescription(answer) // CallManager.shared.handleRemoteDescription(answer)
log("Answer received.") // log("Answer received.")
case .offer(let offer): // case .offer(let offer):
CallManager.shared.handleRemoteDescription(offer) // CallManager.shared.handleRemoteDescription(offer)
log("Offer received.") // log("Offer received.")
case .bye: // case .bye:
disconnect() // disconnect()
default: // default:
break // break
} // }
} // }
//
// MARK: Streaming // // MARK: Streaming
func webSocketDidConnect(_ webSocket: WebSocket) { // func webSocketDidConnect(_ webSocket: WebSocket) {
guard let info = currentRoomInfo else { return } // guard let info = currentRoomInfo else { return }
log("Connected to web socket.") // log("Connected to web socket.")
let message = [ // let message = [
"cmd": "register", // "cmd": "register",
"roomid": info.roomID, // "roomid": info.roomID,
"clientid": info.clientID // "clientid": info.clientID
] // ]
guard let data = try? JSONSerialization.data(withJSONObject: message, options: [.prettyPrinted]) else { return } // guard let data = try? JSONSerialization.data(withJSONObject: message, options: [.prettyPrinted]) else { return }
webSocket.send(data) // webSocket.send(data)
if isInitiator { // if isInitiator {
CallManager.shared.initiateCall().retainUntilComplete() // CallManager.shared.initiateCall().retainUntilComplete()
} // }
drainMessageQueue() // drainMessageQueue()
} // }
//
func webSocket(_ webSocket: WebSocket, didReceive message: String) { // func webSocket(_ webSocket: WebSocket, didReceive message: String) {
log("Received data from web socket.") // log("Received data from web socket.")
handle(message) // handle(message)
CallManager.shared.drainMessageQueue() // CallManager.shared.drainICECandidateQueue()
} // }
//
func webSocketDidDisconnect(_ webSocket: WebSocket) { // func webSocketDidDisconnect(_ webSocket: WebSocket) {
webSocket.delegate = nil // webSocket.delegate = nil
log("Disconnecting from web socket.") // log("Disconnecting from web socket.")
} // }
//
func callManager(_ callManager: CallManager, sendData data: Data) { // func callManager(_ callManager: CallManager, sendData data: Data) {
guard let info = currentRoomInfo else { return } // guard let info = currentRoomInfo else { return }
TestCallServer.send(data, roomID: info.roomID, userID: info.clientID).retainUntilComplete() // TestCallServer.send(data, roomID: info.roomID, userID: info.clientID).retainUntilComplete()
} // }
//
// MARK: Camera // // MARK: Camera
func captureVideoOutput(sampleBuffer: CMSampleBuffer) { // func captureVideoOutput(sampleBuffer: CMSampleBuffer) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } // guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let ciImage = CIImage(cvImageBuffer: pixelBuffer) // let ciImage = CIImage(cvImageBuffer: pixelBuffer)
let image = UIImage(ciImage: ciImage) // let image = UIImage(ciImage: ciImage)
DispatchQueue.main.async { [weak self] in // DispatchQueue.main.async { [weak self] in
self?.previewView.image = image // self?.previewView.image = image
} // }
} // }
//
// MARK: Logging // // MARK: Logging
private func log(_ string: String) { // private func log(_ string: String) {
DispatchQueue.main.async { [weak self] in // DispatchQueue.main.async { [weak self] in
guard let self = self else { return } // guard let self = self else { return }
self.infoTextView.text = self.infoTextView.text + "\n" + string // self.infoTextView.text = self.infoTextView.text + "\n" + string
} // }
} // }
} //}

@ -0,0 +1,14 @@
import WebRTC
extension CallVCV2 : CameraManagerDelegate {
func handleVideoOutputCaptured(sampleBuffer: CMSampleBuffer) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let rtcPixelBuffer = RTCCVPixelBuffer(pixelBuffer: pixelBuffer)
let timestamp = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
let timestampNs = Int64(timestamp * 1000000000)
let frame = RTCVideoFrame(buffer: rtcPixelBuffer, rotation: RTCVideoRotation._0, timeStampNs: timestampNs)
frame.timeStamp = Int32(timestamp)
callManager.handleLocalFrameCaptured(frame)
}
}

@ -0,0 +1,31 @@
import WebRTC
extension CallVCV2 : CallManagerDelegate {
/// Invoked by `CallManager` upon initiating or accepting a call. This method sends the SDP to the other
/// party before streaming starts.
func sendSDP(_ sdp: RTCSessionDescription) {
guard let room = room else { return }
let json = [
"type" : RTCSessionDescription.string(for: sdp.type),
"sdp" : sdp.sdp
]
guard let data = try? JSONSerialization.data(withJSONObject: json, options: [ .prettyPrinted ]) else { return }
print("[Calls] Sending SDP to test call server: \(json).")
TestCallServer.send(data, roomID: room.roomID, userID: room.clientID).retainUntilComplete()
}
/// Invoked when the peer connection has generated an ICE candidate.
func sendICECandidate(_ candidate: RTCIceCandidate) {
guard let room = room else { return }
let json = [
"type" : "candidate",
"label" : "\(candidate.sdpMLineIndex)",
"id" : candidate.sdpMid,
"candidate" : candidate.sdp
]
guard let data = try? JSONSerialization.data(withJSONObject: json, options: [ .prettyPrinted ]) else { return }
print("[Calls] Sending ICE candidate to test call server: \(json).")
TestCallServer.send(data, roomID: room.roomID, userID: room.clientID).retainUntilComplete()
}
}

@ -0,0 +1,28 @@
extension CallVCV2 : WebSocketDelegate {
func webSocketDidConnect(_ webSocket: WebSocket) {
guard let room = room else { return }
let json = [
"cmd" : "register",
"roomid" : room.roomID,
"clientid" : room.clientID
]
guard let data = try? JSONSerialization.data(withJSONObject: json, options: [ .prettyPrinted ]) else { return }
print("[Calls] Web socket connected. Sending: \(json).")
webSocket.send(data)
print("[Calls] Is initiator: \(room.isInitiator).")
if room.isInitiator {
callManager.initiateCall().retainUntilComplete()
}
}
func webSocketDidDisconnect(_ webSocket: WebSocket) {
webSocket.delegate = nil
}
func webSocket(_ webSocket: WebSocket, didReceive message: String) {
print("[Calls] Message received through web socket: \(message).")
handle([ message ])
}
}

@ -0,0 +1,95 @@
import WebRTC
final class CallVCV2 : UIViewController {
let roomID = "37923672512" // NOTE: You need to change this every time to ensure the room isn't full
var room: RoomInfo?
var socket: WebSocket?
lazy var callManager: CallManager = {
let result = CallManager()
result.delegate = self
return result
}()
lazy var cameraManager: CameraManager = {
let result = CameraManager()
result.delegate = self
return result
}()
lazy var videoCapturer: RTCVideoCapturer = {
return RTCCameraVideoCapturer(delegate: callManager.localVideoSource)
}()
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpViewHierarchy()
cameraManager.prepare()
touch(videoCapturer)
autoConnectToTestRoom()
}
func setUpViewHierarchy() {
// Create video views
let localVideoView = RTCMTLVideoView()
localVideoView.contentMode = .scaleAspectFill
let remoteVideoView = RTCMTLVideoView()
remoteVideoView.contentMode = .scaleAspectFill
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ localVideoView, remoteVideoView ])
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.alignment = .fill
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.pin(to: view)
// Attach video views
callManager.attachLocalRenderer(localVideoView)
callManager.attachRemoteRenderer(remoteVideoView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
cameraManager.start()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
cameraManager.stop()
}
// MARK: General
func autoConnectToTestRoom() {
// Connect to a random test room
TestCallServer.join(roomID: roomID).done2 { [weak self] room in
print("[Calls] Connected to test room.")
guard let self = self else { return }
self.room = room
if let messages = room.messages {
self.handle(messages)
}
let socket = WebSocket(url: URL(string: room.wssURL)!)
socket.delegate = self
socket.connect()
self.socket = socket
}.catch2 { error in
SNLog("Couldn't join room due to error: \(error).")
}
}
func handle(_ messages: [String]) {
print("[Calls] Handling messages:")
messages.forEach { print("[Calls] \($0)") }
messages.forEach { message in
let signalingMessage = SignalingMessage.from(message: message)
switch signalingMessage {
case .candidate(let candidate): callManager.handleCandidateMessage(candidate)
case .answer(let answer): callManager.handleRemoteDescription(answer)
case .offer(let offer): callManager.handleRemoteDescription(offer)
default: break
}
}
callManager.drainICECandidateQueue()
}
}

@ -2,29 +2,26 @@ import Foundation
import AVFoundation import AVFoundation
@objc @objc
protocol CameraCaptureDelegate : AnyObject { protocol CameraManagerDelegate : AnyObject {
func captureVideoOutput(sampleBuffer: CMSampleBuffer) func handleVideoOutputCaptured(sampleBuffer: CMSampleBuffer)
} }
final class CameraManager : NSObject { final class CameraManager : NSObject {
private let captureSession = AVCaptureSession() private let captureSession = AVCaptureSession()
private let videoDataOutput = AVCaptureVideoDataOutput() private let videoDataOutput = AVCaptureVideoDataOutput()
private let videoDataOutputQueue
= DispatchQueue(label: "CameraManager.videoDataOutputQueue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
private let audioDataOutput = AVCaptureAudioDataOutput() private let audioDataOutput = AVCaptureAudioDataOutput()
private let dataOutputQueue = DispatchQueue(label: "CameraManager.dataOutputQueue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
private var isCapturing = false private var isCapturing = false
weak var delegate: CameraCaptureDelegate? weak var delegate: CameraManagerDelegate?
private lazy var videoCaptureDevice: AVCaptureDevice? = { private lazy var videoCaptureDevice: AVCaptureDevice? = {
return AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) return AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
}() }()
static let shared = CameraManager()
private override init() { }
func prepare() { func prepare() {
captureSession.sessionPreset = .low print("[Calls] Preparing camera.")
if let videoCaptureDevice = videoCaptureDevice, if let videoCaptureDevice = videoCaptureDevice,
let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice), captureSession.canAddInput(videoInput) { let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice), captureSession.canAddInput(videoInput) {
captureSession.addInput(videoInput) captureSession.addInput(videoInput)
@ -32,30 +29,28 @@ final class CameraManager : NSObject {
if captureSession.canAddOutput(videoDataOutput) { if captureSession.canAddOutput(videoDataOutput) {
captureSession.addOutput(videoDataOutput) captureSession.addOutput(videoDataOutput)
videoDataOutput.videoSettings = [ kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA) ] videoDataOutput.videoSettings = [ kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA) ]
videoDataOutput.setSampleBufferDelegate(self, queue: dataOutputQueue) videoDataOutput.setSampleBufferDelegate(self, queue: videoDataOutputQueue)
videoDataOutput.connection(with: .video)?.videoOrientation = .portrait guard let connection = videoDataOutput.connection(with: AVMediaType.video) else { return }
videoDataOutput.connection(with: .video)?.automaticallyAdjustsVideoMirroring = false connection.videoOrientation = .portrait
videoDataOutput.connection(with: .video)?.isVideoMirrored = true connection.automaticallyAdjustsVideoMirroring = false
connection.isVideoMirrored = true
} else { } else {
SNLog("Couldn't add video data output to capture session.") SNLog("Couldn't add video data output to capture session.")
captureSession.commitConfiguration()
} }
} }
func start() { func start() {
guard !isCapturing else { return } guard !isCapturing else { return }
print("[Calls] Starting camera.")
isCapturing = true isCapturing = true
#if arch(arm64)
captureSession.startRunning() captureSession.startRunning()
#endif
} }
func stop() { func stop() {
guard isCapturing else { return } guard isCapturing else { return }
print("[Calls] Stopping camera.")
isCapturing = false isCapturing = false
#if arch(arm64)
captureSession.stopRunning() captureSession.stopRunning()
#endif
} }
} }
@ -63,7 +58,7 @@ extension CameraManager : AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptur
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard connection == videoDataOutput.connection(with: .video) else { return } guard connection == videoDataOutput.connection(with: .video) else { return }
delegate?.captureVideoOutput(sampleBuffer: sampleBuffer) delegate?.handleVideoOutputCaptured(sampleBuffer: sampleBuffer)
} }
func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { } func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { }

@ -1,60 +1,60 @@
import UIKit //import UIKit
import AVFoundation //import AVFoundation
import WebRTC //import WebRTC
//
final class VideoCallVC : UIViewController { //final class VideoCallVC : UIViewController {
private var localVideoView = UIView() // private var localVideoView = UIView()
private var remoteVideoView = UIView() // private var remoteVideoView = UIView()
//
override func viewDidLoad() { // override func viewDidLoad() {
super.viewDidLoad() // super.viewDidLoad()
setUpViewHierarchy() // setUpViewHierarchy()
CameraManager.shared.delegate = self // CameraManager.shared.delegate = self
} // }
//
private func setUpViewHierarchy() { // private func setUpViewHierarchy() {
// Create video views // // Create video views
#if arch(arm64) // #if arch(arm64)
// Use Metal // // Use Metal
let localRenderer = RTCMTLVideoView(frame: self.localVideoView.frame) // let localRenderer = RTCMTLVideoView(frame: self.localVideoView.frame)
localRenderer.contentMode = .scaleAspectFill // localRenderer.contentMode = .scaleAspectFill
let remoteRenderer = RTCMTLVideoView(frame: self.remoteVideoView.frame) // let remoteRenderer = RTCMTLVideoView(frame: self.remoteVideoView.frame)
remoteRenderer.contentMode = .scaleAspectFill // remoteRenderer.contentMode = .scaleAspectFill
#else // #else
// Use OpenGLES // // Use OpenGLES
let localRenderer = RTCEAGLVideoView(frame: self.localVideoView.frame) // let localRenderer = RTCEAGLVideoView(frame: self.localVideoView.frame)
let remoteRenderer = RTCEAGLVideoView(frame: self.remoteVideoView.frame) // let remoteRenderer = RTCEAGLVideoView(frame: self.remoteVideoView.frame)
#endif // #endif
// Set up stack view // // Set up stack view
let stackView = UIStackView(arrangedSubviews: [ localVideoView, remoteVideoView ]) // let stackView = UIStackView(arrangedSubviews: [ localVideoView, remoteVideoView ])
stackView.axis = .vertical // stackView.axis = .vertical
stackView.distribution = .fillEqually // stackView.distribution = .fillEqually
stackView.alignment = .fill // stackView.alignment = .fill
view.addSubview(stackView) // view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false // stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.pin(to: view) // stackView.pin(to: view)
// Attach video views // // Attach video views
CallManager.shared.attachLocalRenderer(localRenderer) // CallManager.shared.attachLocalRenderer(localRenderer)
CallManager.shared.attachRemoteRenderer(remoteRenderer) // CallManager.shared.attachRemoteRenderer(remoteRenderer)
localVideoView.addSubview(localRenderer) // localVideoView.addSubview(localRenderer)
localRenderer.translatesAutoresizingMaskIntoConstraints = false // localRenderer.translatesAutoresizingMaskIntoConstraints = false
localRenderer.pin(to: localVideoView) // localRenderer.pin(to: localVideoView)
remoteVideoView.addSubview(remoteRenderer) // remoteVideoView.addSubview(remoteRenderer)
remoteRenderer.translatesAutoresizingMaskIntoConstraints = false // remoteRenderer.translatesAutoresizingMaskIntoConstraints = false
remoteRenderer.pin(to: remoteVideoView) // remoteRenderer.pin(to: remoteVideoView)
} // }
} //}
//
// MARK: Camera //// MARK: Camera
extension VideoCallVC : CameraCaptureDelegate { //extension VideoCallVC : CameraCaptureDelegate {
//
func captureVideoOutput(sampleBuffer: CMSampleBuffer) { // func captureVideoOutput(sampleBuffer: CMSampleBuffer) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } // guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let rtcpixelBuffer = RTCCVPixelBuffer(pixelBuffer: pixelBuffer) // let rtcpixelBuffer = RTCCVPixelBuffer(pixelBuffer: pixelBuffer)
let timestamp = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) // let timestamp = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
let timestampNs = Int64(timestamp * 1000000000) // let timestampNs = Int64(timestamp * 1000000000)
let videoFrame = RTCVideoFrame(buffer: rtcpixelBuffer, rotation: RTCVideoRotation._0, timeStampNs: timestampNs) // let videoFrame = RTCVideoFrame(buffer: rtcpixelBuffer, rotation: RTCVideoRotation._0, timeStampNs: timestampNs)
videoFrame.timeStamp = Int32(timestamp) // videoFrame.timeStamp = Int32(timestamp)
CallManager.shared.handleLocalFrameCaptured(videoFrame) // CallManager.shared.handleLocalFrameCaptured(videoFrame)
} // }
} //}

@ -158,7 +158,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
// Get default open group rooms if needed // Get default open group rooms if needed
OpenGroupAPIV2.getDefaultRoomsIfNeeded() OpenGroupAPIV2.getDefaultRoomsIfNeeded()
let callVC = CallVC() let callVC = CallVCV2()
present(callVC, animated: true, completion: nil) present(callVC, animated: true, completion: nil)
} }

@ -3,26 +3,20 @@ import WebRTC
extension CallManager { extension CallManager {
public func handleCandidateMessage(_ candidate: RTCIceCandidate) { public func handleCandidateMessage(_ candidate: RTCIceCandidate) {
print("[Calls] Received ICE candidate message.")
candidateQueue.append(candidate) candidateQueue.append(candidate)
} }
public func handleRemoteDescription(_ sdp: RTCSessionDescription) { public func handleRemoteDescription(_ sdp: RTCSessionDescription) {
print("[Calls] Received remote SDP: \(sdp.sdp).")
peerConnection.setRemoteDescription(sdp, completionHandler: { [weak self] error in peerConnection.setRemoteDescription(sdp, completionHandler: { [weak self] error in
if let error = error { if let error = error {
SNLog("Couldn't set SDP due to error: \(error).") SNLog("Couldn't set SDP due to error: \(error).")
} else { } else {
guard let self = self else { return } guard let self = self,
if sdp.type == .offer, self.peerConnection.localDescription == nil { sdp.type == .offer, self.peerConnection.localDescription == nil else { return }
self.acceptCall() self.acceptCall().retainUntilComplete()
}
} }
}) })
} }
public func drainMessageQueue() {
for candidate in candidateQueue {
peerConnection.add(candidate)
}
candidateQueue.removeAll()
}
} }

@ -3,8 +3,9 @@ import WebRTC
public protocol CallManagerDelegate : AnyObject { public protocol CallManagerDelegate : AnyObject {
var videoCapturer: RTCVideoCapturer { get } var videoCapturer: RTCVideoCapturer { get }
func callManager(_ callManager: CallManager, sendData data: Data) func sendSDP(_ sdp: RTCSessionDescription)
func sendICECandidate(_ candidate: RTCIceCandidate)
} }
/// See https://developer.mozilla.org/en-US/docs/Web/API/RTCSessionDescription for more information. /// See https://developer.mozilla.org/en-US/docs/Web/API/RTCSessionDescription for more information.
@ -78,7 +79,7 @@ public final class CallManager : NSObject, RTCPeerConnectionDelegate {
} }
// MARK: Initialization // MARK: Initialization
internal override init() { public override init() {
super.init() super.init()
let mediaStreamTrackIDS = ["ARDAMS"] let mediaStreamTrackIDS = ["ARDAMS"]
peerConnection.add(audioTrack, streamIds: mediaStreamTrackIDS) peerConnection.add(audioTrack, streamIds: mediaStreamTrackIDS)
@ -97,10 +98,16 @@ public final class CallManager : NSObject, RTCPeerConnectionDelegate {
audioSession.unlockForConfiguration() audioSession.unlockForConfiguration()
} }
public static let shared = CallManager() // MARK: General
public func drainICECandidateQueue() {
print("[Calls] Draining ICE candidate queue.")
candidateQueue.forEach { peerConnection.add($0) }
candidateQueue.removeAll()
}
// MARK: Call Management // MARK: Call Management
public func initiateCall() -> Promise<Void> { public func initiateCall() -> Promise<Void> {
print("[Calls] Initiating call.")
/* /*
guard let thread = TSContactThread.fetch(for: publicKey, using: transaction) else { return Promise(error: Error.noThread) } guard let thread = TSContactThread.fetch(for: publicKey, using: transaction) else { return Promise(error: Error.noThread) }
*/ */
@ -117,8 +124,7 @@ public final class CallManager : NSObject, RTCPeerConnectionDelegate {
} }
} }
let message = sdp.serialize()! self.delegate?.sendSDP(sdp)
self.delegate?.callManager(self, sendData: message)
/* /*
let message = CallMessage() let message = CallMessage()
@ -133,6 +139,7 @@ public final class CallManager : NSObject, RTCPeerConnectionDelegate {
} }
public func acceptCall() -> Promise<Void> { public func acceptCall() -> Promise<Void> {
print("[Calls] Accepting call.")
/* /*
guard let thread = TSContactThread.fetch(for: publicKey, using: transaction) else { return Promise(error: Error.noThread) } guard let thread = TSContactThread.fetch(for: publicKey, using: transaction) else { return Promise(error: Error.noThread) }
*/ */
@ -149,8 +156,7 @@ public final class CallManager : NSObject, RTCPeerConnectionDelegate {
} }
} }
let message = sdp.serialize()! self.delegate?.sendSDP(sdp)
self.delegate?.callManager(self, sendData: message)
/* /*
let message = CallMessage() let message = CallMessage()
@ -195,8 +201,7 @@ public final class CallManager : NSObject, RTCPeerConnectionDelegate {
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) { public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
SNLog("ICE candidate generated.") SNLog("ICE candidate generated.")
let message = candidate.serialize()! delegate?.sendICECandidate(candidate)
delegate?.callManager(self, sendData: message)
} }
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) { public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
@ -207,16 +212,3 @@ public final class CallManager : NSObject, RTCPeerConnectionDelegate {
SNLog("Data channel opened.") SNLog("Data channel opened.")
} }
} }
// MARK: Utilities
extension RTCSessionDescription {
func serialize() -> Data? {
let json = [
"type": RTCSessionDescription.string(for: self.type),
"sdp": self.sdp
]
return try? JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted])
}
}

@ -2,37 +2,36 @@ import Foundation
import WebRTC import WebRTC
public enum SignalingMessage { public enum SignalingMessage {
case none
case candidate(_ message: RTCIceCandidate) case candidate(_ message: RTCIceCandidate)
case answer(_ message: RTCSessionDescription) case answer(_ message: RTCSessionDescription)
case offer(_ message: RTCSessionDescription) case offer(_ message: RTCSessionDescription)
case bye case bye
public static func from(message: String) -> SignalingMessage { public static func from(message: String) -> SignalingMessage? {
guard let data = message.data(using: String.Encoding.utf8) else { return .none } guard let data = message.data(using: String.Encoding.utf8),
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON else { return .none } let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON else { return nil }
let messageAsJSON: JSON let messageAsJSON: JSON
if let foo = json["msg"] as? String { if let string = json["msg"] as? String {
guard let data = foo.data(using: String.Encoding.utf8) else { return .none } guard let data = string.data(using: String.Encoding.utf8),
guard let bar = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON else { return .none } let json = try? JSONSerialization.jsonObject(with: data, options: []) as? JSON else { return nil }
messageAsJSON = bar messageAsJSON = json
} else { } else {
messageAsJSON = json messageAsJSON = json
} }
guard let type = messageAsJSON["type"] as? String else { return .none } guard let type = messageAsJSON["type"] as? String else { return nil }
switch type { switch type {
case "candidate": case "candidate":
guard let candidate = RTCIceCandidate.candidate(from: messageAsJSON) else { return .none } guard let candidate = RTCIceCandidate.candidate(from: messageAsJSON) else { return nil }
return .candidate(candidate) return .candidate(candidate)
case "answer": case "answer":
guard let sdp = messageAsJSON["sdp"] as? String else { return .none } guard let sdp = messageAsJSON["sdp"] as? String else { return nil }
return .answer(RTCSessionDescription(type: .answer, sdp: sdp)) return .answer(RTCSessionDescription(type: .answer, sdp: sdp))
case "offer": case "offer":
guard let sdp = messageAsJSON["sdp"] as? String else { return .none } guard let sdp = messageAsJSON["sdp"] as? String else { return nil }
return .offer(RTCSessionDescription(type: .offer, sdp: sdp)) return .offer(RTCSessionDescription(type: .offer, sdp: sdp))
case "bye": case "bye":
return .bye return .bye
default: return .none default: return nil
} }
} }
} }
@ -46,7 +45,7 @@ extension RTCIceCandidate {
"id": sdpMid, "id": sdpMid,
"candidate": sdp "candidate": sdp
] ]
return try? JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted]) return try? JSONSerialization.data(withJSONObject: json, options: [ .prettyPrinted ])
} }
static func candidate(from json: JSON) -> RTCIceCandidate? { static func candidate(from json: JSON) -> RTCIceCandidate? {

@ -18,7 +18,7 @@ public final class WebSocket : NSObject, SRWebSocketDelegate {
socket.delegate = self socket.delegate = self
} }
public func connect(url: URL) { public func connect() {
socket.open() socket.open()
} }
@ -26,6 +26,10 @@ public final class WebSocket : NSObject, SRWebSocketDelegate {
socket.send(data) socket.send(data)
} }
public func webSocketDidOpen(_ webSocket: SRWebSocket!) {
delegate?.webSocketDidConnect(self)
}
public func webSocket(_ webSocket: SRWebSocket!, didReceiveMessage message: Any!) { public func webSocket(_ webSocket: SRWebSocket!, didReceiveMessage message: Any!) {
guard let message = message as? String else { return } guard let message = message as? String else { return }
delegate?.webSocket(self, didReceive: message) delegate?.webSocket(self, didReceive: message)
Loading…
Cancel
Save