SimpleX-Chat/apps/ios/Shared/Views/Call/CallManager.swift
Stanislav Dmitrenko 54020250dc
ios: native WebRTC (#1933)
* ios: native WebRTC

* add video showing

* make async function better working with main thread

* wrapped code in main actor, just in case

* small change

* a little better

* enable relay

* removed unused code

* allow switching calls

* testing

* enable encryption

* testing more

* another test

* one more test

* fix remote unencrypted video

* deleted unused code related to PixelBuffer

* added MediaEncryption playground

* better playground

* better playground

* fixes

* use new encryption api

* media encryption works

* small changes

* added lib dependency

* use commit reference for lib instead of version

* video format, PIP size

* remove sample.js

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-03-02 13:17:01 +00:00

118 lines
4 KiB
Swift

//
// CallManager.swift
// SimpleX (iOS)
//
// Created by Evgeny on 22/05/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import Foundation
import SimpleXChat
class CallManager {
func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> UUID {
let uuid = UUID()
let call = Call(direction: .outgoing, contact: contact, callkitUUID: uuid, callState: .waitCapabilities, localMedia: media)
call.speakerEnabled = media == .video
ChatModel.shared.activeCall = call
return uuid
}
func startOutgoingCall(callUUID: UUID) -> Bool {
let m = ChatModel.shared
if let call = m.activeCall, call.callkitUUID == callUUID {
m.showCallView = true
m.callCommand = .capabilities(media: call.localMedia)
return true
}
return false
}
func answerIncomingCall(callUUID: UUID) -> Bool {
if let invitation = getCallInvitation(callUUID) {
answerIncomingCall(invitation: invitation)
return true
}
return false
}
func answerIncomingCall(invitation: RcvCallInvitation) {
let m = ChatModel.shared
m.callInvitations.removeValue(forKey: invitation.contact.id)
let call = Call(
direction: .incoming,
contact: invitation.contact,
callkitUUID: invitation.callkitUUID,
callState: .invitationAccepted,
localMedia: invitation.callType.media,
sharedKey: invitation.sharedKey
)
call.speakerEnabled = invitation.callType.media == .video
m.activeCall = call
m.showCallView = true
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
let iceServers = getIceServers()
logger.debug("answerIncomingCall useRelay: \(useRelay)")
logger.debug("answerIncomingCall iceServers: \(String(describing: iceServers))")
m.callCommand = .start(
media: invitation.callType.media,
aesKey: invitation.sharedKey,
iceServers: iceServers,
relay: useRelay
)
}
func endCall(callUUID: UUID, completed: @escaping (Bool) -> Void) {
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
endCall(call: call) { completed(true) }
} else if let invitation = getCallInvitation(callUUID) {
endCall(invitation: invitation) { completed(true) }
} else {
completed(false)
}
}
func endCall(call: Call, completed: @escaping () -> Void) {
let m = ChatModel.shared
if case .ended = call.callState {
logger.debug("CallManager.endCall: call ended")
m.activeCall = nil
m.showCallView = false
completed()
} else {
logger.debug("CallManager.endCall: ending call...")
m.callCommand = .end
m.showCallView = false
Task {
do {
try await apiEndCall(call.contact)
} catch {
logger.error("CallController.provider apiEndCall error: \(responseError(error))")
}
DispatchQueue.main.async {
m.activeCall = nil
completed()
}
}
}
}
func endCall(invitation: RcvCallInvitation, completed: @escaping () -> Void) {
ChatModel.shared.callInvitations.removeValue(forKey: invitation.contact.id)
Task {
do {
try await apiRejectCall(invitation.contact)
} catch {
logger.error("CallController.provider apiRejectCall error: \(responseError(error))")
}
completed()
}
}
private func getCallInvitation(_ callUUID: UUID) -> RcvCallInvitation? {
if let (_, invitation) = ChatModel.shared.callInvitations.first(where: { (_, inv) in inv.callkitUUID == callUUID }) {
return invitation
}
return nil
}
}