你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
与 CallKit 集成
本文档介绍如何将 CallKit 与 iOS 应用程序集成。
先决条件
- 具有活动订阅的 Azure 帐户。 免费创建帐户。
- 已部署的通信服务资源。 创建通信服务资源。
- 用于启用通话客户端的用户访问令牌。 有关详细信息,请参阅创建和管理访问令牌。
- 可选:完成快速入门向应用程序添加语音呼叫
CallKit 集成(在 SDK 中)
Azure 通信服务 iOS SDK 中的 CallKit 集成为我们处理与 CallKit 的交互。 若要执行静音/取消静音、保留/恢复等任何调用操作,只需在 Azure 通信服务 SDK 上调用 API。
使用 CallKitOptions 初始化调用代理
使用配置的实例 CallKitOptions
,我们可以创建 CallAgent
处理 CallKit
方式。
let options = CallAgentOptions()
let callKitOptions = CallKitOptions(with: createProviderConfig())
options.callKitOptions = callKitOptions
// Configure the properties of `CallKitOptions` instance here
self.callClient!.createCallAgent(userCredential: userCredential,
options: options,
completionHandler: { (callAgent, error) in
// Initialization
})
指定传出呼叫的呼叫收件人信息
首先,我们需要为传出呼叫JoinCallOptions()
或组呼叫创建实例StartCallOptions()
:
let options = StartCallOptions()
或
let options = JoinCallOptions()
然后创建 的实例 CallKitRemoteInfo
options.callKitRemoteInfo = CallKitRemoteInfo()
- 分配用于
callKitRemoteInfo.displayNameForCallKit
自定义呼叫收件人的显示名称并配置CXHandle
值的值。 在displayNameForCallKit
最后一个拨号呼叫日志中,指定的值正是它显示的方式。 在上次拨入的呼叫日志中。
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
cxHandle
分配值是用户回叫该联系人时应用程序接收的值
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
为传入呼叫指定呼叫收件人信息
首先,我们需要创建以下项的 CallKitOptions
实例:
let callKitOptions = CallKitOptions(with: createProviderConfig())
配置实例的属性 CallKitOptions
:
收到传入呼叫时,SDK 将调用传递给变量 provideRemoteInfo
的块,我们需要获取传入调用方(我们需要传递给 CallKit)的显示名称。
callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
func provideCallKitRemoteInfo(callerInfo: CallerInfo) -> CallKitRemoteInfo
{
let callKitRemoteInfo = CallKitRemoteInfo()
callKitRemoteInfo.displayName = "CALL_TO_PHONENUMBER_BY_APP"
callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
return callKitRemoteInfo
}
配置音频会话
在拨打或接受传入呼叫之前,以及在保持呼叫后恢复呼叫之前,将调用音频会话。
callKitOptions.configureAudioSession = self.configureAudioSession
public func configureAudioSession() -> Error? {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
var configError: Error?
do {
try audioSession.setCategory(.playAndRecord)
} catch {
configError = error
}
return configError
}
注意:如果 Contoso 已配置音频会话,则不要提供 nil
音频会话,但在块中返回 nil
错误
callKitOptions.configureAudioSession = self.configureAudioSession
public func configureAudioSession() -> Error? {
return nil
}
如果 nil
为 configureAudioSession
SDK 提供,则 SDK 调用 SDK 中的默认实现。
处理传入的推送通知有效负载
当应用收到传入的推送通知有效负载时,我们需要调用 handlePush
以处理它。 Azure 通信服务调用 SDK 将引发事件IncomingCall
。
public func handlePushNotification(_ pushPayload: PKPushPayload)
{
let callNotification = PushNotificationInfo.fromDictionary(pushPayload.dictionaryPayload)
if let agent = self.callAgent {
agent.handlePush(notification: callNotification) { (error) in }
}
}
// Event raised by the SDK
public func callAgent(_ callAgent: CallAgent, didRecieveIncomingCall incomingcall: IncomingCall) {
}
当应用关闭或其他情况时,我们可用于 reportIncomingCall
处理推送通知。
if let agent = self.callAgent {
/* App is not in a killed state */
agent.handlePush(notification: callNotification) { (error) in }
} else {
/* App is in a killed state */
CallClient.reportIncomingCall(with: callNotification, callKitOptions: callKitOptions) { (error) in
if (error == nil) {
DispatchQueue.global().async {
self.callClient = CallClient()
let options = CallAgentOptions()
let callKitOptions = CallKitOptions(with: createProviderConfig())
callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
callKitOptions.configureAudioSession = self.configureAudioSession
options.callKitOptions = callKitOptions
self.callClient!.createCallAgent(userCredential: userCredential,
options: options,
completionHandler: { (callAgent, error) in
if (error == nil) {
self.callAgent = callAgent
self.callAgent!.handlePush(notification: callNotification) { (error) in }
}
})
}
} else {
os_log("SDK couldn't handle push notification", log:self.log)
}
}
}
CallKit 集成(在应用中)
如果要在应用中集成 CallKit,而不在 SDK 中使用 CallKit 实现,请参阅此处的快速入门示例。 但要处理的重要事情之一是在正确的时间启动音频。 如下所示
let outgoingAudioOptions = OutgoingAudioOptions()
outgoingAudioOptions.muted = true
let incomingAudioOptions = IncomingAudioOptions()
incomingAudioOptions.muted = true
var copyAcceptCallOptions = AcceptCallOptions()
copyStartCallOptions.outgoingAudioOptions = outgoingAudioOptions
copyStartCallOptions.incomingAudioOptions = incomingAudioOptions
callAgent.startCall(participants: participants,
options: copyStartCallOptions,
completionHandler: completionBlock)
静音扬声器和麦克风可确保在 CallKit 调用 didActivateAudioSession
打开 CXProviderDelegate
之前不使用物理音频设备。 否则,呼叫可能会被删除,否则音频将不起作用。
何时 didActivateAudioSession
应启动音频流。
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
Task {
guard let activeCall = await self.callKitHelper.getActiveCall() else {
print("No active calls found when activating audio session !!")
return
}
try await startAudio(call: activeCall)
}
}
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
Task {
guard let activeCall = await self.callKitHelper.getActiveCall() else {
print("No active calls found when deactivating audio session !!")
return
}
try await stopAudio(call: activeCall)
}
}
private func stopAudio(call: Call) async throws {
try await self.callKitHelper.muteCall(callId: call.id, isMuted: true)
try await call.stopAudio(stream: call.activeOutgoingAudioStream)
try await call.stopAudio(stream: call.activeIncomingAudioStream)
try await call.muteIncomingAudio()
}
private func startAudio(call: Call) async throws {
try await call.startAudio(stream: LocalOutgoingAudioStream())
try await self.callKitHelper.muteCall(callId: call.id, isMuted: false)
try await call.startAudio(stream: RemoteIncomingAudioStream())
try await call.unmuteIncomingAudio()
}
在 CallKit 不调用 didActivateAudioSession
的情况下,在停止音频之前,还必须将传出音频静音。 然后,用户可以手动取消静音麦克风。
注意
在某些情况下,CallKit 不会调用 didActivateAudioSession
,即使应用具有提升的音频权限,在这种情况下,音频将保持静音,直到收到回叫。 UI 必须反映扬声器和麦克风的状态。 呼叫中的远程参与者/秒将看到用户也已静音音频。 在这种情况下,用户必须手动取消静音。