你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
建议订阅通话 SDK 事件。 Azure 通信服务 SDK 是动态的,包含可能会随时间变化的属性。 可以订阅这些事件,以便提前收到任何更改的通知。 按照本文中的说明订阅 Azure 通信服务 SDK 事件。
Azure 通信通话 SDK 上的事件
本部分介绍应用可以订阅的事件和属性更改。 订阅这些事件可使应用了解调用 SDK 中的状态更改并相应地做出响应。
跟踪事件至关重要,因为它使应用程序的状态能够与 Azure 通信服务呼叫框架的状态保持同步。 跟踪事件有助于在不对 SDK 对象实现拉取机制的情况下对更改进行堆栈。
本节假设您已完成快速入门,或者您实现了一个可以拨打和接听通话的应用程序。 如果尚未完成入门指南,请参阅 向应用添加语音呼叫。
JavaScript 调用 SDK 中的每个对象都有 properties
和 collections
。 其值在对象的整个生存期内都会更改。
使用on()
方法订阅对象事件。 使用off()
方法解除对对象事件的订阅。
属性
你可以订阅 '<property>Changed'
事件以侦听相应属性上的值变化。
属性订阅示例
在以下示例中,我们订阅了 isLocalVideoStarted
属性的值变化。
call.on('isLocalVideoStartedChanged', () => {
// At that point the value call.isLocalVideoStarted is updated
console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`);
});
集合
可以订阅事件 \<collection>Updated
,以接收有关对象集合中更改的通知。
\<collection>Updated
每当要监视的集合中添加或删除元素时,都会触发该事件。
-
'<collection>Updated'
事件的负载具有added
数组,其中包含添加到集合的值。 -
'<collection>Updated'
事件的负载还具有removed
数组,其中包含从集合中删除的值。
集合订阅示例
在以下示例中,我们订阅了通话对象 LocalVideoStream
的值变化。
call.on('localVideoStreamsUpdated', updateEvent => {
updateEvent.added.forEach(async (localVideoStream) => {
// Contains an array of LocalVideoStream that were added to the call
// Add a preview and start any processing if needed
handleAddedLocalVideoStream(localVideoStream )
});
updateEvent.removed.forEach(localVideoStream => {
// Contains an array of LocalVideoStream that were removed from the call
// Remove the preview and stop any processing if needed
handleRemovedLocalVideoStream(localVideoStream )
});
});
CallAgent
对象上的事件
事件名称:incomingCall
客户端收到传入呼叫时触发 incomingCall
事件。
应用程序对事件的反应如何?
应用程序必须将传入呼叫通知接收方。 通知提示需要使收件人能够接受或拒绝呼叫。
代码示例:
callClient.on('incomingCall', (async (incomingCallEvent) => {
try {
// Store a reference to the call object
incomingCall = incomingCallEvent.incomingCall;
// Update your UI to allow
acceptCallButton.disabled = false;
callButton.disabled = true;
} catch (error) {
console.error(error);
}
});
事件名称:callsUpdated
callsUpdated
更新事件在移除通话或将其添加到通话代理时触发。 当用户拨打、接听或终止通话时,便会发生此事件。
应用程序对事件的反应如何?
应用程序必须根据 CallAgent 实例的活动调用数更新其 UI。
事件名称:connectionStateChanged
connectionStateChanged
事件将在 CallAgent
的信号状态发生更新时触发。
应用程序对事件的反应如何?
应用程序必须基于新状态更新其 UI。 可能的连接状态值为 Connected
和 Disconnected
。
代码示例:
callClient.on('connectionStateChanged', (async (connectionStateChangedEvent) => {
if (connectionStateChangedEvent.newState === "Connected") {
enableCallControls() // Enable all UI element that allow user to make a call
}
if (connectionStateChangedEvent.newState === 'Disconnected') {
if (typeof connectionStateChangedEvent.reason !== 'undefined') {
alert(`Disconnected reason: ${connectionStateChangedEvent.reason}`)
}
disableCallControls() // Disable all the UI element that allows the user to make a call
}
});
Call
对象上的事件
事件名称:stateChanged
当调用stateChanged
状态发生更改时,会触发该事件。 例如,当通话的状态从 connected
变为 disconnected
时。
应用程序对事件的反应如何?
应用程序必须相应地更新其 UI。 根据新的通话状态禁用或启用相应的按钮和其他 UI 元素。
代码示例:
call.on('stateChanged', (async (connectionStateChangedEvent) => {
if(call.state === 'Connected') {
connectedLabel.hidden = false;
acceptCallButton.disabled = true;
startCallButton.disabled = true;
startVideoButton.disabled = false;
stopVideoButton.disabled = false
} else if (call.state === 'Disconnected') {
connectedLabel.hidden = true;
startCallButton.disabled = false;
console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`);
}
});
事件:idChanged
idChanged
事件在通话 ID 发生变化时触发。 当通话状态从 connecting
变为 connected
时,通话的 ID 将发生变化。 在通话完成连接后,通话的 ID 将保持不变。
应用程序对事件的反应如何?
应用程序可以保存新的呼叫 ID,或在需要时从调用对象检索它。
代码示例:
let callId = "";
call.on('idChanged', (async (callIdChangedEvent) => {
callId = call.id; // You can log it as the call ID is useful for debugging call issues
});
事件:isMutedChanged
isMutedChanged
事件在本地音频静音或取消静音时触发。
应用程序对事件的反应如何?
应用程序必须将静音/取消静音按钮更新为正确的状态。
代码示例:
call.on('isMutedChanged', (async (isMutedChangedEvent) => {
microphoneButton.disabled = call.isMuted;
});
事件:isScreenSharingOnChanged
当启用或禁用本地用户的屏幕共享时,将触发 isScreenSharingOnChanged
事件。
应用程序对事件的反应如何?
如果屏幕共享处于打开状态,应用程序必须向用户显示预览和/或警告。
如果屏幕共享关闭,应用程序必须删除预览和警告。
代码示例:
call.on('isScreenSharingOnChanged', () => {
if (!this.call.isScreenSharing) {
displayStartScreenSharingButton();
hideScreenSharingWarning()
removeScreenSharingPreview();
} else {
displayScreenSharingWarning()
displayStopScreenSharingButton();
renderScreenSharingPreview();
}
});
事件:isLocalVideoStartedChanged
当用户启用或禁用其本地视频时,isLocalVideoStartedChanged
事件将触发。
应用程序对事件的反应如何?
应用程序必须显示本地视频的预览,并启用或禁用相机激活按钮。
代码示例:
call.on('isLocalVideoStartedChanged', () => {
showDisableCameraButton(call.isLocalVideoStarted);
});
事件:remoteParticipantsUpdated
应用程序必须订阅每个已添加的 RemoteParticipants
的事件,并取消订阅离开通话的参与者的事件。
应用程序对事件的反应如何?
应用程序必须显示本地视频的预览,并启用或禁用相机激活按钮。
代码示例:
call.on('remoteParticipantsUpdated', (remoteParticipantsUpdatedEvent) => {
remoteParticipantsUpdatedEvent.added.forEach(participant => {
// handleParticipant should
// - subscribe to the remote participants events
// - update the UI
handleParticipant(participant);
});
remoteParticipantsUpdatedEvent.removed.forEach(participant => {
// removeParticipant should
// - unsubscribe from the remote participants events
// - update the UI
removeParticipant(participant);
});
});
事件:localVideoStreamsUpdated
localVideoStreamsUpdated
事件将在本地视频流列表发生更改时触发。 当用户启动或移除视频流时,便会发生此类变化。
应用程序对事件的反应如何?
对于添加的每个 LocalVideoStream
,应用程序必须为其显示预览。 应用程序必须移除预览,并在每个 LocalVideoStream
被删除时停止处理。
代码示例:
call.on('localVideoStreamsUpdated', (localVideoStreamUpdatedEvent) => {
localVideoStreamUpdatedEvent.added.forEach(addedLocalVideoStream => {
// Add a preview and start any processing if needed
handleAddedLocalVideoStream(addedLocalVideoStream)
});
localVideoStreamUpdatedEvent.removed.forEach(removedLocalVideoStream => {
// Remove the preview and stop any processing if needed
this.handleRemovedLocalVideoStream(removedLocalVideoStream)
});
});
事件:remoteAudioStreamsUpdated
当远程 remoteAudioStreamsUpdated
音频流列表发生变化时,该事件将被触发。 当远程参与者在通话中添加或移除音频流时,便会发生此类变化。
应用程序对事件的反应如何?
如果一个正在处理的流被删除,则必须停止处理。 另一方面,如果添加了流,则事件接收是开始处理新音频流的好地方。
事件:totalParticipantCountChanged
totalParticipantCountChanged
将在通话中的 totalParticipant 数量发生变化时触发。
应用程序对事件的反应如何?
如果应用程序显示参与者计数器,应用程序可以在收到事件时更新其参与者计数器。
代码示例:
call.on('totalParticipantCountChanged', () => {
participantCounterElement.innerText = call.totalParticipantCount;
});
事件:roleChanged
roleChanged
参与者将在通话中的 localParticipant 角色发生变化时触发。 例如,当本地参与者在通话中成为演示者 ACSCallParticipantRolePresenter
时。
应用程序对事件的反应如何?
应用程序必须基于用户新角色启用或禁用该按钮。
代码示例:
call.on('roleChanged', () => {
this.roleElement = call.role;
});
事件:mutedByOthers
当本地参与者将通话中的其他参与者静音时,就会发生mutedByOthers
事件。
应用程序对事件的反应如何?
应用程序必须向用户显示一条消息,通知他们已静音。
代码示例:
call.on('mutedByOthers', () => {
messageBanner.innerText = "You have been muted by other participant in this call";
});
事件:callerInfoChanged
更新主叫方信息时发生 callerInfoChanged
事件。 当调用方更改其显示名称时,会发生这种情况。
应用程序对事件的反应如何? 应用程序可以更新主叫方信息。
代码示例:
call.on('callerInfoChanged', () => {
showCallerInfo(call.callerInfo)
});
事件:transferorInfoChanged
更新转接方信息时发生 transferorInfoChanged
事件。 当转接方更改其显示名称时,会发生这种情况。
应用程序对事件的反应如何? 应用程序可以更新转接方信息。
代码示例:
call.on('transferorInfoChanged', () => {
showTransferorInfo(call.transferorInfo)
});
RemoteParticipant
对象上的事件
事件:roleChanged
roleChanged
事件将在通话中的 RemoteParticipant
角色发生变化时触发。 例如,当 RemoteParticipant 在通话中成为演示者 ACSCallParticipantRolePresenter
时。
应用程序对事件的反应如何?
应用程序必须基于 RemoteParticipant
新角色更新其 UI。
代码示例:
remoteParticipant.on('roleChanged', () => {
updateRole(remoteParticipant);
});
事件:isMutedChanged
isMutedChanged
事件将在其中一个 RemoteParticipant
静音或取消静音其麦克风时触发。
应用程序对事件的反应如何?
应用程序可以在显示参与者的视图附近显示图标。
代码示例:
remoteParticipant.on('isMutedChanged', () => {
updateMuteStatus(remoteParticipant); // Update the UI based on the mute state of the participant
});
事件:displayNameChanged
displayNameChanged
事件将在 RemoteParticipant
的名称发生更新时触发。
应用程序对事件的反应如何?
如果应用程序显示在 UI 中,则必须更新参与者的名称。
代码示例:
remoteParticipant.on('displayNameChanged', () => {
remoteParticipant.nameLabel.innerText = remoteParticipant.displayName;
});
事件:isSpeakingChanged
isSpeakingChanged
事件将在通话的主导发言人发生变化时触发。
应用程序对事件的反应如何?
应用程序 UI 必须优先显示成为主导发言人的 RemoteParticipant
。
代码示例:
remoteParticipant.on('isSpeakingChanged', () => {
showAsRemoteSpeaker(remoteParticipant) // Display a speaking icon near the participant
});
事件:videoStreamsUpdated
videoStreamsUpdated
事件将在远程参与者在通话中添加或移除 VideoStream 时触发。
应用程序对事件的反应如何?
如果应用程序正在处理已删除的流,则应用程序必须停止处理。 添加新的流时,我们建议应用程序开始呈现或处理它。
代码示例:
remoteParticipant.on('videoStreamsUpdated', (videoStreamsUpdatedEvent) => {
videoStreamsUpdatedEvent.added.forEach(addedRemoteVideoStream => {
// Remove a renderer and start processing the stream if any processing is needed
handleAddedRemoteVideoStream(addedRemoteVideoStream)
});
videoStreamsUpdatedEvent.removed.forEach(removedRemoteVideoStream => {
// Remove the renderer and stop processing the stream if any processing is ongoing
this.handleRemovedRemoteVideoStream(removedRemoteVideoStream)
});
});
AudioEffectsFeature
对象上的事件
事件:effectsStarted
当所选音频效果应用于音频流时,便会发生此事件。 例如,当有人开启“噪声抑制”时,将触发 effectsStarted
。
应用程序对事件的反应如何?
应用程序可以显示或启用允许用户禁用音频效果的按钮。
代码示例:
audioEffectsFeature.on('effectsStarted', (effects) => {
stopEffectButton.style.visibility = "visible";
});
事件:effectsStopped
当所选音频效果应用于音频流时,便会发生此事件。 例如,当有人关闭“噪音抑制”时,将触发 effectsStopped
。
应用程序对事件的反应如何?
应用程序可以显示或启用允许用户启用音频效果的按钮。
代码示例:
audioEffectsFeature.on('effectsStopped', (effects) => {
startEffectButton.style.visibility = "visible";
});
事件:effectsError
如果启动或应用音频效果时发生错误,便会发生此事件。
应用程序对事件的反应如何?
应用程序必须显示警报或错误消息,指出音频效果未按预期工作。
代码示例:
audioEffectsFeature.on('effectsError', (error) => {
console.log(`Error with the audio effect ${error}`);
alert(`Error with the audio effect`);
});
安装 SDK
找到项目级 build.gradle
文件,并将 mavenCentral()
添加到 buildscript
和 allprojects
下的存储库列表中:
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
然后,在模块级 build.gradle
文件中,将以下行添加到 dependencies
部分:
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0'
...
}
初始化所需的对象
若要创建 CallAgent
实例,必须对 createCallAgent
实例调用 CallClient
方法。 此调用将异步返回 CallAgent
实例对象。
createCallAgent
方法采用 CommunicationUserCredential
作为参数来封装访问令牌。
若要访问 DeviceManager
,必须先创建 callAgent
实例。 然后,可以使用 CallClient.getDeviceManager
方法获取 DeviceManager
。
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential).get();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
若要为主叫方设置显示名称,请使用以下替代方法:
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgentOptions callAgentOptions = new CallAgentOptions();
callAgentOptions.setDisplayName("Alice Bob");
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential, callAgentOptions).get();
安装 Android SDK 后,可以订阅大多数属性和集合,以在值更改时通知这些属性和集合。
属性
若要订阅 property changed
事件,请采取以下操作:
// subscribe
PropertyChangedListener callStateChangeListener = new PropertyChangedListener()
{
@Override
public void onPropertyChanged(PropertyChangedEvent args)
{
Log.d("The call state has changed.");
}
}
call.addOnStateChangedListener(callStateChangeListener);
//unsubscribe
call.removeOnStateChangedListener(callStateChangeListener);
使用在同一类中定义的事件侦听器时,请将侦听器绑定到变量。 若要添加和删除侦听器方法,请将变量作为参数传入。
如果尝试直接将侦听器作为参数传入,你会丢失对该侦听器的引用。 Java 将创建这些侦听器的新实例,而不是引用以前创建的实例。 它们仍会正常触发,但无法移除,因为你不再有对它们的引用。
集合
若要订阅 collection updated
事件,请采取以下操作:
LocalVideoStreamsChangedListener localVideoStreamsChangedListener = new LocalVideoStreamsChangedListener()
{
@Override
public void onLocalVideoStreamsUpdated(LocalVideoStreamsEvent localVideoStreamsEventArgs) {
Log.d(localVideoStreamsEventArgs.getAddedStreams().size());
Log.d(localVideoStreamsEventArgs.getRemovedStreams().size());
}
}
call.addOnLocalVideoStreamsChangedListener(localVideoStreamsChangedListener);
// To unsubscribe
call.removeOnLocalVideoStreamsChangedListener(localVideoStreamsChangedListener);
设置系统
若要设置系统,请按照以下步骤操作。
创建 Xcode 项目
在 Xcode 中,创建新的 iOS 项目,并选择“单视图应用”模板。 本文使用 SwiftUI 框架,因此应将“语言”设置为“Swift”,并将“界面”设置为“SwiftUI”。
在本文中,无需创建测试。 请随意清除“包括测试”复选框。
使用 CocoaPods 安装包和依赖项
为应用程序创建 Podfile,如此示例所示:
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
运行
pod install
。使用 Xcode 打开
.xcworkspace
。
请求访问麦克风
若要访问设备的麦克风,需要使用 NSMicrophoneUsageDescription
更新应用的信息属性列表。 将关联的值设置为一个字符串,该字符串将包含在系统用于向用户请求访问权限的对话框中。
右键单击项目树的 Info.plist 条目,然后选择“打开为...”“源代码”>。 将以下代码行添加到顶层 <dict>
节,然后保存文件。
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
设置应用框架
打开项目的 ContentView.swift
文件。 将 import
声明添加到文件顶部以导入 AzureCommunicationCalling
库。 此外,导入 AVFoundation
。 你需要使用它来处理代码中的音频权限请求。
import AzureCommunicationCalling
import AVFoundation
初始化 CallAgent
若要从 CallAgent
创建 CallClient
实例,必须使用 callClient.createCallAgent
方法,该方法在初始化后异步返回 CallAgent
对象。
若要创建通话客户端,请传递 CommunicationTokenCredential
对象:
import AzureCommunication
let tokenString = "token_string"
var userCredential: CommunicationTokenCredential?
do {
let options = CommunicationTokenRefreshOptions(initialToken: token, refreshProactively: true, tokenRefresher: self.fetchTokenSync)
userCredential = try CommunicationTokenCredential(withOptions: options)
} catch {
updates("Couldn't created Credential object", false)
initializationDispatchGroup!.leave()
return
}
// tokenProvider needs to be implemented by Contoso, which fetches a new token
public func fetchTokenSync(then onCompletion: TokenRefreshOnCompletion) {
let newToken = self.tokenProvider!.fetchNewToken()
onCompletion(newToken, nil)
}
将创建的 CommunicationTokenCredential
对象传递给 CallClient
并设置显示名称:
self.callClient = CallClient()
let callAgentOptions = CallAgentOptions()
options.displayName = " iOS Azure Communication Services User"
self.callClient!.createCallAgent(userCredential: userCredential!,
options: callAgentOptions) { (callAgent, error) in
if error == nil {
print("Create agent succeeded")
self.callAgent = callAgent
} else {
print("Create agent failed")
}
})
安装 iOS SDK 后,可以订阅大多数属性和集合,以在值更改时通知这些属性和集合。
属性
若要订阅 property changed
事件,请使用以下代码。
call.delegate = self
// Get the property of the call state by getting on the call's state member
public func call(_ call: Call, didChangeState args: PropertyChangedEventArgs) {
{
print("Callback from SDK when the call state changes, current state: " + call.state.rawValue)
}
// to unsubscribe
self.call.delegate = nil
集合
若要订阅 collection updated
事件,请使用以下代码。
call.delegate = self
// Collection contains the streams that were added or removed only
public func call(_ call: Call, didUpdateLocalVideoStreams args: LocalVideoStreamsUpdatedEventArgs) {
{
print(args.addedStreams.count)
print(args.removedStreams.count)
}
// to unsubscribe
self.call.delegate = nil