239 lines
8.8 KiB
Swift
239 lines
8.8 KiB
Swift
//
|
|
// 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) }
|
|
}
|
|
}
|