VoIPCallStateManager.swift 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2019-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import Foundation
  21. @objc class VoIPCallStateManager: NSObject {
  22. @objc static let shared = VoIPCallStateManager()
  23. private var callQueue = Queue<Any>()
  24. private let lockQueue = DispatchQueue(label: "CallManagerLockQueue")
  25. private let managerQueue = DispatchQueue(label: "CallManagerProcessQueue")
  26. private var ringingTimer: Timer?
  27. private var callService = VoIPCallService()
  28. @objc required override init() {
  29. super.init()
  30. callService.delegate = self
  31. }
  32. /**
  33. Get the current state of a call
  34. - Returns: CallState
  35. */
  36. @objc func currentCallState() -> VoIPCallService.CallState {
  37. return callService.currentState()
  38. }
  39. /**
  40. Get the current contact of a call
  41. - Returns: Contact
  42. */
  43. @objc func currentCallContact() -> Contact? {
  44. return callService.currentContact()
  45. }
  46. /**
  47. Get the current callId of a call
  48. - Returns: VoIPCallId
  49. */
  50. @objc func currentCallId() -> VoIPCallId? {
  51. return callService.currentCallId()
  52. }
  53. /**
  54. Is initiator of the current call
  55. - Returns: true or false
  56. */
  57. @objc func isCallInitiator() -> Bool {
  58. return callService.isCallInitiator()
  59. }
  60. /**
  61. Is the current call muted
  62. - Returns: true or false
  63. */
  64. @objc func isCallMuted() -> Bool {
  65. return callService.isCallMuted()
  66. }
  67. /**
  68. Is the speaker for the current call active
  69. - Returns: true or false
  70. */
  71. @objc func isSpeakerActive() -> Bool {
  72. return callService.isSpeakerActive()
  73. }
  74. /**
  75. Set the rtc audio session
  76. - parameter audioSession: audio session from callkit
  77. */
  78. @objc func setRTCAudio(_ audioSession: AVAudioSession) {
  79. callService.setRTCAudioSession(audioSession)
  80. }
  81. /**
  82. Set the audio session for RTC active
  83. - parameter audioSession: Set the audio session from callkit
  84. */
  85. @objc func activateRTCAudio() {
  86. callService.activateRTCAudio()
  87. }
  88. /**
  89. Is the current call already accepted
  90. - Returns: true or false
  91. */
  92. @objc func isCallAlreadyAccepted() -> Bool {
  93. return callService.isCallAlreadyAccepted()
  94. }
  95. /**
  96. Present the CallViewController
  97. */
  98. @objc func presentCallViewController() {
  99. callService.presentCallViewController()
  100. }
  101. /**
  102. Dismiss the CallViewController
  103. */
  104. @objc func dismissCallViewController() {
  105. callService.dismissCallViewController()
  106. }
  107. /**
  108. Start capture local video
  109. */
  110. @objc func startCaptureLocalVideo(renderer: RTCVideoRenderer, useBackCamera: Bool = false, switchCamera: Bool = false) {
  111. callService.startCaptureLocalVideo(renderer: renderer, useBackCamera: useBackCamera, switchCamera: switchCamera)
  112. }
  113. /**
  114. End capture local video
  115. */
  116. @objc func endCaptureLocalVideo(switchCamera: Bool = false) {
  117. callService.endCaptureLocalVideo(switchCamera: switchCamera)
  118. }
  119. /**
  120. Get local video
  121. */
  122. @objc func localVideoRenderer() -> RTCVideoRenderer? {
  123. return callService.localVideoRenderer()
  124. }
  125. /**
  126. Start render remote video
  127. */
  128. @objc func renderRemoteVideo(to renderer: RTCVideoRenderer) {
  129. callService.renderRemoteVideo(to: renderer)
  130. }
  131. /**
  132. End capture local video
  133. */
  134. @objc func endRemoteVideo() {
  135. callService.endRemoteVideo()
  136. }
  137. /**
  138. Get remote video
  139. */
  140. @objc func remoteVideoRenderer() -> RTCVideoRenderer? {
  141. return callService.remoteVideoRenderer()
  142. }
  143. /**
  144. Get peer video quality profile
  145. */
  146. func remoteVideoQualityProfile() -> CallsignalingProtocol.ThreemaVideoCallQualityProfile? {
  147. return callService.remoteVideoQualityProfile()
  148. }
  149. /**
  150. Get peer is using turn server
  151. */
  152. func networkIsRelayed() -> Bool {
  153. return callService.networkIsRelayed()
  154. }
  155. /**
  156. Add a user action to the process queue
  157. - parameter action: VoIPCallUserAction
  158. */
  159. @objc func processUserAction(_ action: VoIPCallUserAction) {
  160. addMessageToQueue(message: action)
  161. }
  162. /**
  163. Add a incoming call offer to the process queue
  164. - parameter offer: VoIPCallOfferMessage
  165. - parameter contact: Contact from the offer
  166. - parameter completion: Completion block
  167. */
  168. @objc func incomingCallOffer(offer: VoIPCallOfferMessage, contact theContact: Contact, completion: (() -> Void)?) {
  169. BackgroundTaskManager.shared.newBackgroundTask(key: kAppVoIPIncomCallBackgroundTask, timeout: Int(kAppVoIPIncomCallBackgroundTaskTime)) {
  170. if completion != nil {
  171. offer.completion = completion
  172. }
  173. offer.contact = theContact
  174. self.addMessageToQueue(message: offer)
  175. }
  176. }
  177. /**
  178. Add a incoming call answer to the process queue
  179. - parameter answer: VoIPCallAnswerMessage
  180. - parameter contact: Contact from the answer
  181. - parameter completion: Completion block
  182. */
  183. @objc func incomingCallAnswer(answer: VoIPCallAnswerMessage, contact theContact: Contact, completion: (() -> Void)?) {
  184. BackgroundTaskManager.shared.newBackgroundTask(key: kAppVoIPBackgroundTask, timeout: Int(kAppPushBackgroundTaskTime)) {
  185. if completion != nil {
  186. answer.completion = completion
  187. }
  188. answer.contact = theContact
  189. self.addMessageToQueue(message: answer)
  190. }
  191. }
  192. /**
  193. Add a incoming ringing message to the process queue
  194. - parameter ringing: VoIPCallRingingMessage
  195. */
  196. @objc func incomingCallRinging(ringing: VoIPCallRingingMessage) {
  197. BackgroundTaskManager.shared.newBackgroundTask(key: kAppVoIPBackgroundTask, timeout: Int(kAppPushBackgroundTaskTime)) {
  198. self.addMessageToQueue(message: ringing)
  199. }
  200. }
  201. /**
  202. Add a incoming hangup message to the process queue
  203. - parameter hangup: VoIPCallHangupMessage
  204. */
  205. @objc func incomingCallHangup(hangup: VoIPCallHangupMessage) {
  206. BackgroundTaskManager.shared.newBackgroundTask(key: kAppVoIPBackgroundTask, timeout: Int(kAppPushBackgroundTaskTime)) {
  207. self.addMessageToQueue(message: hangup)
  208. }
  209. }
  210. /**
  211. Add a incoming ice candidates message to the process queue
  212. - parameter candidates: VoIPCallIceCandidatesMessage
  213. - parameter contact: Contact from the ice candidates
  214. - parameter completion: Completion block
  215. */
  216. @objc func incomingIceCandidates(candidates: VoIPCallIceCandidatesMessage, contact theContact: Contact, completion: (() -> Void)?) {
  217. BackgroundTaskManager.shared.newBackgroundTask(key: kAppVoIPBackgroundTask, timeout: Int(kAppPushBackgroundTaskTime)) {
  218. if completion != nil {
  219. candidates.completion = completion
  220. }
  221. candidates.contact = theContact
  222. self.addMessageToQueue(message: candidates)
  223. }
  224. }
  225. }
  226. extension VoIPCallStateManager {
  227. // MARK: Private functions
  228. /**
  229. Add a message to the process queue and start process
  230. - parameter message: Any message
  231. */
  232. private func addMessageToQueue(message: Any) {
  233. var queueCountBefore = 0
  234. lockQueue.sync {
  235. DDLogNotice("Threema call: VoIPCallStateManager -> add message to queue \(message.self)");
  236. queueCountBefore = callQueue.elements.count
  237. callQueue.enqueue(message)
  238. }
  239. if queueCountBefore == 0 {
  240. processQueue()
  241. }
  242. }
  243. /**
  244. Start the process queue on CallService
  245. */
  246. private func processQueue() {
  247. var element: Any?
  248. lockQueue.sync {
  249. element = callQueue.dequeue()
  250. }
  251. if element != nil {
  252. managerQueue.async {
  253. DDLogNotice("Threema call: VoIPCallStateManager -> start process");
  254. self.callService.startProcess(element: element!)
  255. }
  256. }
  257. }
  258. }
  259. extension VoIPCallStateManager: VoIPCallServiceDelegate {
  260. /**
  261. Delegate from VoIPCallServiceDelegate
  262. Process next message if queue is not empty
  263. */
  264. func callServiceFinishedProcess() {
  265. DDLogNotice("Threema call: VoIPCallStateManager -> finished process, check next");
  266. if callQueue.elements.count > 0 {
  267. processQueue()
  268. }
  269. }
  270. }
  271. protocol Enqueuable {
  272. associatedtype Element
  273. mutating func enqueue(_ element: Element)
  274. func peek() -> Element?
  275. mutating func dequeue() -> Element?
  276. mutating func removeAll()
  277. }
  278. struct Queue<T>: Enqueuable {
  279. typealias Element = T
  280. internal var elements = [Element]()
  281. internal mutating func enqueue(_ element: Element) {
  282. elements.append(element)
  283. }
  284. internal func peek() -> Element? {
  285. return elements.first
  286. }
  287. internal mutating func dequeue() -> Element? {
  288. guard elements.isEmpty == false else {
  289. return nil
  290. }
  291. return elements.removeFirst()
  292. }
  293. internal mutating func removeAll() {
  294. elements.removeAll()
  295. }
  296. }
  297. extension Queue: CustomStringConvertible {
  298. var description: String {
  299. return "\(elements)"
  300. }
  301. }