<kbd id="9plqc"><label id="9plqc"></label></kbd>

        <th id="9plqc"></th>
        1. <center id="9plqc"><video id="9plqc"></video></center>
          <sub id="9plqc"><form id="9plqc"><pre id="9plqc"></pre></form></sub>
          <nav id="9plqc"><form id="9plqc"><legend id="9plqc"></legend></form></nav>
          ios 您所在的位置:網(wǎng)站首頁 女孩屬鼠二月出生是犯月嗎 ios

          ios

          2024-12-26 16:13| 來源: 網(wǎng)絡整理| 查看: 265

          作者簡介:龔宇華,聲網(wǎng) Agora.io 首席 iOS 研發(fā)工程師,負責 iOS 端移動應用產(chǎn)品設計和技術架構。 簡介

          CallKit 是蘋果在 iOS10 中推出的,專為 VoIP 通話場景設計的系統(tǒng)框架,在 iOS 上為 VoIP 通話提供了系統(tǒng)級的支持。

          在 iOS10 以前,VoIP 場景的體驗存在很多局限。比如沒有專門的來電呼叫通知方式,App 在后臺接收到來電呼叫后,只能使用一般的系統(tǒng)通知方式提示用戶。如果用戶關掉了通知權限,就會錯過來電。VoIP 通話本身也很容易被打斷。比如用戶在通話過程中打開了另一個使用音頻設備的應用,或者接到了一個運營商電話,VoIP 通話就會被打斷。

          為了改善 VoIP 通話的用戶體驗問題,CallKit 框架在系統(tǒng)層面把 VoIP 通話提高到了和運營商通話一樣的級別。當 App 收到來電呼叫后,可以通過 CallKit 把 VoIP 通話注冊給系統(tǒng),讓系統(tǒng)使用和運營商電話一樣的界面提示用戶。在通話過程中,app 的音視頻權限也變成和運營商電話一樣,不會被其他應用打斷。VoIP 通話過程中接到運營商電話時,在界面上由用戶自己選擇是否掛起 /掛斷當前的 VoIP 通話。

          另外,使用了 CallKit 框架的 VoIP 通話也會和運營商電話一樣出現(xiàn)在系統(tǒng)的電話記錄中。用戶可以直接在通訊錄和電話記錄中發(fā)起新的 VoIP 呼叫。

          因此,一個有 VoIP 通話場景的應用應該盡快集成 CallKit,以大幅提高用戶體驗和使用便捷性。

          下面我們就來看下 CallKit 的使用方法,并且把它集成到一個使用 Agora SDK 的視頻通話應用中。

          CallKit 基本類介紹

          CallKit 最重要的類有兩個,CXProvider 和 CXCallController。這兩個類是 CallKit 框架的核心。

          CXProvider

          CXProvider 主要負責通話流程的控制,向系統(tǒng)注冊通話和更新通話的連接狀態(tài)等。重要的 api 有下面這些:

          open class CXProvider : NSObject { /// 初始化方法 public init(configuration: CXProviderConfiguration) /// 設置回調對象 open func setDelegate(_ delegate: CXProviderDelegate?, queue: DispatchQueue?) /// 向系統(tǒng)注冊一個來電。如果注冊成功,系統(tǒng)就會根據(jù) CXCallUpdate 中的信息彈出來電畫面 open func reportNewIncomingCall(with UUID: UUID, update: CXCallUpdate, completion: @escaping (Error?) -> Swift.Void) /// 更新一個通話的信息 open func reportCall(with UUID: UUID, updated update: CXCallUpdate) /// 告訴系統(tǒng)通話開始連接 open func reportOutgoingCall(with UUID: UUID, startedConnectingAt dateStartedConnecting: Date?) /// 告訴系統(tǒng)通話連接成功 open func reportOutgoingCall(with UUID: UUID, connectedAt dateConnected: Date?) /// 告訴系統(tǒng)通話結束 open func reportCall(with UUID: UUID, endedAt dateEnded: Date?, reason endedReason: CXCallEndedReason) }

          可以看到,CXProvider 使用 UUID 來標識一個通話,使用 CXCallUpdate 類來設置通話的屬性。開發(fā)者可以使用正確格式的字符串為每個通話創(chuàng)建對應的 UUID;也可以直接使用系統(tǒng)創(chuàng)建的 UUID。

          用戶在系統(tǒng)界面上對通話進行的作都通過 CXProviderDelegate 中的回調方法通知應用。

          CXCallController

          CXCallController 主要負責執(zhí)行對通話的作。

          open class CXCallController : NSObject { /// 初始化方法 public convenience init() /// 獲取 callObserver,通過 callObserver 可以得到系統(tǒng)所有進行中的通話的 uuid 和通話狀態(tài) open var callObserver: CXCallObserver { get } /// 執(zhí)行對一個通話的作 open func request(_ transaction: CXTransaction, completion: @escaping (Error?) -> Swift.Void) }

          其中 CXTransaction 是一個作的封裝,包含了動作 CXAction 和通話 UUID。發(fā)起通話、接聽通話、掛斷通話、靜音通話等動作都有對應的 CXAction 子類。

          和 Agora SDK 結合

          下面我們看下怎么在一個使用 Agora SDK 的視頻通話應用中集成 CallKit。Demo 的完整代碼見 Github 地址

          實現(xiàn)視頻通話

          首先快速實現(xiàn)一個視頻通話的功能。

          使用 AppId 創(chuàng)建 AgoraRtcEngineKit 實例:

          private lazy var rtcEngine: AgoraRtcEngineKit = AgoraRtcEngineKit.sharedEngine(withAppId: , delegate: self)

          設置 ChannelProfile 和本地預覽視圖:

          override func viewDidLoad() { super.viewDidLoad() rtcEngine.setChannelProfile(.communication) let canvas = AgoraRtcVideoCanvas() canvas.uid = 0 canvas.view = localVideoView canvas.renderMode = .hidden rtcEngine.setupLocalVideo(canvas) }

          在 AgoraRtcEngineDelegate 的遠端用戶加入頻道事件中設置遠端視圖:

          extension ViewController: AgoraRtcEngineDelegate { func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) { let canvas = AgoraRtcVideoCanvas() canvas.uid = uid canvas.view = remoteVideoView canvas.renderMode = .hidden engine.setupRemoteVideo(canvas) remoteUid = uid remoteVideoView.isHidden = false } }

          實現(xiàn)通話開始、靜音、結束的方法:

          extension ViewController { func startSession(_ session: String) { rtcEngine.startPreview() rtcEngine.joinChannel(byToken: nil, channelId: session, info: nil, uid: 0, joinSuccess: nil) } func muteAudio(_ mute: Bool) { rtcEngine.muteLocalAudioStream(mute) } func stopSession() { remoteVideoView.isHidden = true rtcEngine.leaveChannel(nil) rtcEngine.stopPreview() } }

          至此,一個簡單的視頻通話應用搭建就完成了。雙方只要調用 startSession(_:) 方法加入同一個頻道,就可以進行視頻通話。

          來電顯示

          我們首先創(chuàng)建一個專門的類 CallCenter 來統(tǒng)一管理 CXProvider 和 CXCallController。

          class CallCenter: NSObject { fileprivate let controller = CXCallController() private let provider = CXProvider(configuration: CallCenter.providerConfiguration) private static var providerConfiguration: CXProviderConfiguration { let appName = "AgoraRTCWithCallKit" let providerConfiguration = CXProviderConfiguration(localizedName: appName) providerConfiguration.supportsVideo = true providerConfiguration.maximumCallsPerCallGroup = 1 providerConfiguration.maximumCallGroups = 1 providerConfiguration.supportedHandleTypes = [.phoneNumber] if let iconMaskImage = UIImage(named: ) { providerConfiguration.iconTemplateImageData = UIImagePNGRepresentation(iconMaskImage) } providerConfiguration.ringtoneSound = return providerConfiguration } }

          其中 providerConfiguration 設置了 CallKit 向系統(tǒng)注冊通話時需要的一些基本屬性。比如 localizedName 告訴系統(tǒng)向用戶顯示應用的名稱。iconTemplateImage 給系統(tǒng)提供一張圖片,以在鎖屏的通話界面中顯示。ringtoneSound 是自定義來電響鈴文件。

          接著,我們創(chuàng)建一個接收到呼叫后把呼叫通過 CallKit 注冊給系統(tǒng)的方法。

          func showIncomingCall(of session: String) { let callUpdate = CXCallUpdate() callUpdate.remoteHandle = CXHandle(type: .phoneNumber, value: session) callUpdate.localizedCallerName = session callUpdate.hasVideo = true callUpdate.supportsDTMF = false let uuid = pairedUUID(of: session) provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in if let error = error { print("reportNewIncomingCall error: \(error.localizedDescription)") } }) }

          簡單起見,我們用對方的手機號碼字符串做為通話 session 標示,并構造一個簡單的 session 和 UUID 匹配查詢系統(tǒng)。最后在調用了 CXProvider 的 reportNewIncomingCall(with:update:completion:) 方法后,系統(tǒng)就會根據(jù) CXCallUpdate 中的信息,彈出和運營商電話類似的界面提醒用戶。用戶可以接聽或者拒接,也可以點擊第六個按鈕打開 app。

          接聽 /掛斷通話

          用戶在系統(tǒng)界面上點擊“接受”或“拒絕”按鈕后,CallKit 會通過 CXProviderDelegate 的相關回調通知 app。

          func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { guard let session = pairedSession(of:action.callUUID) else { action.fail() return } delegate?.callCenter(self, answerCall: session) action.fulfill() } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { guard let session = pairedSession(of:action.callUUID) else { action.fail() return } delegate?.callCenter(self, declineCall: session) action.fulfill() }

          通過回調傳入的 CXAction 對象,我們可以知道用戶的作類型以及通話對應的 UUID。最后通過我們自己定義的 CallCenterDelegate 回調通知到 app 的 ViewController 中。

          發(fā)起通話 /靜音 /結束通話

          使用 CXStartCallAction 構造一個 CXTransaction,我們就可以用 CXCallController 的 request(_:completion:) 方法向系統(tǒng)注冊一個發(fā)起的通話。

          func startOutgoingCall(of session: String) { let handle = CXHandle(type: .phoneNumber, value: session) let uuid = pairedUUID(of: session) let startCallAction = CXStartCallAction(call: uuid, handle: handle) startCallAction.isVideo = true let transaction = CXTransaction(action: startCallAction) controller.request(transaction) { (error) in if let error = error { print("startOutgoingSession failed: \(error.localizedDescription)") } } }

          同樣的,我們可以用 CXSetMutedCallAction 和 CXEndCallAction 來靜音 /結束通話。

          func muteAudio(of session: String, muted: Bool) { let muteCallAction = CXSetMutedCallAction(call: pairedUUID(of: session), muted: muted) let transaction = CXTransaction(action: muteCallAction) controller.request(transaction) { (error) in if let error = error { print("muteSession \(muted) failed: \(error.localizedDescription)") } } } func endCall(of session: String) { let endCallAction = CXEndCallAction(call: pairedUUID(of: session)) let transaction = CXTransaction(action: endCallAction) controller.request(transaction) { error in if let error = error { print("endSession failed: \(error.localizedDescription)") } } } 模擬來電和呼叫

          真實的 VoIP 應用需要使用信令系統(tǒng)或者 iOS 的 PushKit 推送,來實現(xiàn)通話呼叫。為了簡單起見,我們在 Demo 上添加了兩個按鈕,直接模擬收到了新的通話呼叫和呼出新的通話。

          private lazy var callCenter = CallCenter(delegate: self) @IBAction func doCallOutPressed(_ sender: UIButton) { callCenter.startOutgoingCall(of: session) } @IBAction func doCallInPressed(_ sender: UIButton) { callCenter.showIncomingCall(of: session) }

          接著通過實現(xiàn) CallCenterDelegate 回調,調用我們前面已經(jīng)預先實現(xiàn)了的使用 Agora SDK 進行視頻通話功能,一個完整的 CallKit 視頻應用就完成了。

          extension ViewController: CallCenterDelegate { func callCenter(_ callCenter: CallCenter, startCall session: String) { startSession(session) } func callCenter(_ callCenter: CallCenter, answerCall session: String) { startSession(session) callCenter.setCallConnected(of: session) } func callCenter(_ callCenter: CallCenter, declineCall session: String) { print("call declined") } func callCenter(_ callCenter: CallCenter, muteCall muted: Bool, session: String) { muteAudio(muted) } func callCenter(_ callCenter: CallCenter, endCall session: String) { stopSession() } }

          通話過程中在音頻外放的狀態(tài)下鎖屏,會顯示類似運營商電話的通話界面。不過可惜的是,目前 CallKit 還不支持像 FaceTime 那樣的在鎖屏下顯示視頻的功能。

          通訊錄 /系統(tǒng)通話記錄

          使用了 CallKit 的 VoIP 通話會出現(xiàn)在用戶系統(tǒng)的通話記錄中,用戶可以像運營商電話一樣直接點擊通話記錄發(fā)起新的 VoIP 呼叫。同時用戶通訊錄中也會有對應的選項讓用戶直接使用支持 CallKit 的應用發(fā)起呼叫。

          實現(xiàn)這個功能并不復雜。無論用戶是點擊通信錄中按鈕,還是點擊通話記錄,系統(tǒng)都會啟動打開對應 app,并觸發(fā) UIApplicationDelegate 的 application(_:continue:restorationHandler:) 回調。我們可以在這個回調方法中獲取到被用戶點擊的電話號碼,并開始 VoIP 通話。

          func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { guard let interaction = userActivity.interaction else { return false } var phoneNumber: String? if let callIntent = interaction.intent as? INStartVideoCallIntent { phoneNumber = callIntent.contacts?.first?.personHandle?.value } else if let callIntent = interaction.intent as? INStartAudioCallIntent { phoneNumber = callIntent.contacts?.first?.personHandle?.value } let callVC = window?.rootViewController as? ViewController callVC?.applyContinueUserActivity(toCall:phoneNumber) return true } extension ViewController { func applyContinueUserActivity(toCall phoneNumber: String?) { guard let phoneNumber = phoneNumber, !phoneNumber.isEmpty else { return } phoneNumberTextField.text = phoneNumber callCenter.startOutgoingCall(of: session) } } 一些注意點 必需在項目的后臺模式設置中啟用 VoIP 模式,才可以正常使用 CallKit 的相關功能。這個模式需要在 Info.plist 文件的 UIBackgroundModes 字段下添加 voip 項來開啟。 如果沒有開啟后臺 VoIP 模式,調用 reportNewIncomingCall(with:update:completion:) 等方法不會有效果。 當發(fā)起通話時,在使用 CXStartCallAction 向系統(tǒng)注冊通話后,系統(tǒng)會啟動應用的 AudioSession,并將其優(yōu)先級提高到運營商通話的級別。如果應用在這個過程中自己對 AudioSession 進行設置作,很可能會導致 AudioSession 啟動失敗。所以應用需要等系統(tǒng)啟動 AudioSession 完成,在收到 CXProviderDelegate 的 provider(_:didActive:) 回調后,再進行 AudioSession 相關的設置。我們在 Demo 中是通過 Agora SDK 的 disableAudio() 和 enableAudio() 等接口來處理這部分邏輯的。 集成 CallKit 后,VoIP 來電也會和運營商電話一樣受到用戶系統(tǒng) “勿擾” 等設置的影響。 在聽筒模式下按鎖屏鍵,系統(tǒng)會按照掛斷處理。這個行為也和運營商電話一致。

          最后再次附上完整 Demo Github 地址



          【本文地址】

          公司簡介

          聯(lián)系我們

          今日新聞

          推薦新聞

          專題文章
            CopyRight 2018-2019 實驗室設備網(wǎng) 版權所有
            黄色免费网站在线看,韩国精品在线观看,韩国美女一区二区,99国产热 吉安县| 宜春市| 梅河口市| 紫云| 根河市| 星子县| 镇雄县| 离岛区| 体育| 万荣县| 志丹县| 吉林省| 伊春市| 宁城县| 建宁县| 沙田区| 班玛县| 武夷山市| 自贡市| 施甸县| 昆明市| 施秉县| 梅州市| 兖州市| 古丈县| 肥东县| 壶关县| 兴仁县| 鄂尔多斯市| 武胜县| 石台县| 黎城县| 进贤县| 曲麻莱县| 杭锦旗| 杭州市| 南丹县| 星座| 诸暨市| 吉隆县| 拉萨市| http://444 http://444 http://444 http://444 http://444 http://444