// // OutgoingCall.swift // OutgoingCall tutorial // // Created by QuentinArguillere on 08/09/2021. // Copyright © 2021 BelledonneCommunications. All rights reserved. // import linphonesw class OutgoingCallTutorialContext : ObservableObject { var mCore: Core! @Published var coreVersion: String = Core.getVersion var mAccount: Account? var mCoreDelegate : CoreDelegate! @Published var username : String = "user" @Published var passwd : String = "pwd" @Published var domain : String = "sip.example.org" @Published var loggedIn: Bool = false @Published var transportType : String = "TLS" // Outgoing call related variables @Published var callMsg : String = "" @Published var isCallRunning : Bool = false @Published var isVideoEnabled : Bool = false @Published var canChangeCamera : Bool = false @Published var remoteAddress : String = "sip:calldest@sip.linphone.org" init() { LoggingService.Instance.logLevel = LogLevel.Debug try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) // Here we enable the video capture & display at Core level // It doesn't mean calls will be made with video automatically, // But it allows to use it later mCore.videoCaptureEnabled = true mCore.videoDisplayEnabled = true // When enabling the video, the remote will either automatically answer the update request // or it will ask it's user depending on it's policy. // Here we have configured the policy to always automatically accept video requests mCore.videoActivationPolicy!.automaticallyAccept = true // If you don't want to automatically accept, // you'll have to use a code similar to the one in toggleVideo to answer a received request // If the following property is enabled, it will automatically configure created call params with video enabled //core.videoActivationPolicy.automaticallyInitiate = true try? mCore.start() mCoreDelegate = CoreDelegateStub( onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in // This function will be called each time a call state changes, // which includes new incoming/outgoing calls self.callMsg = message if (state == .OutgoingInit) { // First state an outgoing call will go through } else if (state == .OutgoingProgress) { // Right after outgoing init } else if (state == .OutgoingRinging) { // This state will be reached upon reception of the 180 RINGING } else if (state == .Connected) { // When the 200 OK has been received } else if (state == .StreamsRunning) { // This state indicates the call is active. // You may reach this state multiple times, for example after a pause/resume // or after the ICE negotiation completes // Wait for the call to be connected before allowing a call update self.isCallRunning = true // Only enable toggle camera button if there is more than 1 camera // We check if core.videoDevicesList.size > 2 because of the fake camera with static image created by our SDK (see below) self.canChangeCamera = core.videoDevicesList.count > 2 } else if (state == .Paused) { // When you put a call in pause, it will became Paused self.canChangeCamera = false } else if (state == .PausedByRemote) { // When the remote end of the call pauses it, it will be PausedByRemote } else if (state == .Updating) { // When we request a call update, for example when toggling video } else if (state == .UpdatedByRemote) { // When the remote requests a call update } else if (state == .Released) { // Call state will be released shortly after the End state self.isCallRunning = false self.canChangeCamera = false } else if (state == .Error) { } }, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") if (state == .Ok) { self.loggedIn = true } else if (state == .Cleared) { self.loggedIn = false } }) mCore.addDelegate(delegate: mCoreDelegate) } func login() { do { var transport : TransportType if (transportType == "TLS") { transport = TransportType.Tls } else if (transportType == "TCP") { transport = TransportType.Tcp } else { transport = TransportType.Udp } let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) let accountParams = try mCore.createAccountParams() let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) try! accountParams.setIdentityaddress(newValue: identity) let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) try address.setTransport(newValue: transport) try accountParams.setServeraddress(newValue: address) accountParams.registerEnabled = true mAccount = try mCore.createAccount(params: accountParams) mCore.addAuthInfo(info: authInfo) try mCore.addAccount(account: mAccount!) mCore.defaultAccount = mAccount } catch { NSLog(error.localizedDescription) } } func unregister() { if let account = mCore.defaultAccount { let params = account.params let clonedParams = params?.clone() clonedParams?.registerEnabled = false account.params = clonedParams } } func delete() { if let account = mCore.defaultAccount { mCore.removeAccount(account: account) mCore.clearAccounts() mCore.clearAllAuthInfo() } } func outgoingCall() { do { // As for everything we need to get the SIP URI of the remote and convert it to an Address let remoteAddress = try Factory.Instance.createAddress(addr: remoteAddress) // We also need a CallParams object // Create call params expects a Call object for incoming calls, but for outgoing we must use null safely let params = try mCore.createCallParams(call: nil) // We can now configure it // Here we ask for no encryption but we could ask for ZRTP/SRTP/DTLS params.mediaEncryption = MediaEncryption.None // If we wanted to start the call with video directly //params.videoEnabled = true // Finally we start the call let _ = mCore.inviteAddressWithParams(addr: remoteAddress, params: params) // Call process can be followed in onCallStateChanged callback from core listener } catch { NSLog(error.localizedDescription) } } func terminateCall() { do { if (mCore.callsNb == 0) { return } // If the call state isn't paused, we can get it using core.currentCall let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0] // Terminating a call is quite simple if let call = coreCall { try call.terminate() } } catch { NSLog(error.localizedDescription) } } func toggleVideo() { do { if (mCore.callsNb == 0) { return } let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0] // We will need the CAMERA permission for video call if let call = coreCall { // To update the call, we need to create a new call params, from the call object this time let params = try mCore.createCallParams(call: call) // Here we toggle the video state (disable it if enabled, enable it if disabled) // Note that we are using currentParams and not params or remoteParams // params is the object you configured when the call was started // remote params is the same but for the remote // current params is the real params of the call, resulting of the mix of local & remote params params.videoEnabled = !(call.currentParams!.videoEnabled) isVideoEnabled = params.videoEnabled // Finally we request the call update try call.update(params: params) // Note that when toggling off the video, TextureViews will keep showing the latest frame displayed } } catch { NSLog(error.localizedDescription) } } func toggleCamera() { do { // Currently used camera let currentDevice = mCore.videoDevice // Let's iterate over all camera available and choose another one for camera in mCore.videoDevicesList { // All devices will have a "Static picture" fake camera, and we don't want to use it if (camera != currentDevice && camera != "StaticImage: Static picture") { try mCore.setVideodevice(newValue: camera) break } } } catch { NSLog(error.localizedDescription) } } func pauseOrResume() { do { if (mCore.callsNb == 0) { return } let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0] if let call = coreCall { if (call.state != Call.State.Paused && call.state != Call.State.Pausing) { // If our call isn't paused, let's pause it try call.pause() } else if (call.state != Call.State.Resuming) { // Otherwise let's resume it try call.resume() } } } catch { NSLog(error.localizedDescription) } } }