你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

管理通话

本文介绍如何使用 Azure 通信服务呼叫 SDK 管理呼叫。 主题包括如何拨打呼叫、管理参与者和管理属性。

先决条件

支持

下表定义了对 Azure 通信服务中的分组讨论室的支持。

标识和通话类型

下表显示了对特定通话类型和标识功能的支持。

标识 Teams 会议 房间 1 对 1 通话 群组通话 1:1 Teams 互操作通话 小组 Teams 互操作通话
通信服务用户 ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
Microsoft 365 用户 ✔️ ✔️ ✔️

操作

下表显示了对调用 SDK 中与单个标识类型相关的单个 API 的支持。

操作 通信服务用户 Microsoft 365 用户
发起对通信服务用户的呼叫 ✔️
发起对 Microsoft 365 用户的呼叫 ✔️ ✔️
发起对电话号码的呼叫 ✔️ ✔️
加入会议室 ✔️
加入 Teams 会议 ✔️ ✔️
基于 groupId 加入通话 ✔️
接受或拒绝传入呼叫 ✔️ ✔️
搁置和恢复通话 ✔️ ✔️
获取参与者 ✔️ ✔️
添加通信服务用户 ✔️
删除通信服务用户 ✔️ ✔️
添加或删除 Microsoft 365 用户 ✔️ ✔️
添加或删除电话号码 ✔️ ✔️
将远程参与者设为静音或取消静音 ✔️ [1] ✔️ [1]
挂断 ✔️ ✔️
结束每个人的通话 ✔️ [2] ✔️

[1] 仅群组通话、会议室和 Teams 会议支持此 API。 [2] 会议室不支持此 API。

SDK

下表显示了各个 Azure 通信服务 SDK 的功能支持。

支持状态 网络 网页版用户界面 iOS iOS 用户界面 安卓 安卓用户界面 Windows操作系统
支持 ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️

安装 SDK

使用 npm install 命令安装适用于 JavaScript 的 Azure 通信服务通用 SDK 和通话 SDK:

npm install @azure/communication-common --save
npm install @azure/communication-calling --save

初始化所需的对象

大多数通话操作需要 CallClient 实例。 创建新的 CallClient 实例时,可以使用自定义选项(如 Logger 实例)对其进行配置。

有了 CallClient 实例后,可以通过调用 CallAgent 创建 createCallAgent 实例。 此方法将异步返回 CallAgent 实例对象。

createCallAgent 方法使用 CommunicationTokenCredential 作为参数。 它接受用户访问令牌

可在 getDeviceManager 实例上使用 CallClient 方法来访问 deviceManager

const { CallClient } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential} = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");

// Set the logger's log level
setLogLevel('verbose');

// Redirect log output to console, file, buffer, REST API, or whatever ___location you want
AzureLogger.log = (...args) => {
    console.log(...args); // Redirect log output to console
};

const userToken = '<USER_TOKEN>';
callClient = new CallClient(options);
const tokenCredential = new AzureCommunicationTokenCredential(userToken);
const callAgent = await callClient.createCallAgent(tokenCredential, {displayName: 'optional Azure Communication Services user name'});
const deviceManager = await callClient.getDeviceManager()

管理与Microsoft基础结构的 SDK 连接

Call Agent 实例可帮助你管理通话(以加入或启动通话)。 通话 SDK 需要连接到 Microsoft 基础结构以获取传入通话通知并协调其他通话详细信息,否则无法工作。 你的 Call Agent 有两种可能的状态:

已连接 - Call Agent connectionStatue 值为 Connected 表示客户端 SDK 已连接,能够接收来自 Microsoft 基础结构的通知。

已断开连接 - Call Agent connectionStatue 值为 Disconnected 表示存在阻止 SDK 正确连接的问题。 应重新创建 Call Agent

  • invalidToken:如果令牌已过期或无效,Call Agent 实例会断开连接并出现此错误。
  • connectionIssue:如果客户端在连接到 Microsoft 基础结构时出现问题,则在多次重试后,Call Agent 会显示 connectionIssue 错误。

可以通过检查 Call Agent 属性的当前值来检查本地 connectionState 是否已连接到 Microsoft 基础结构。 在通话过程中,可以监听 connectionStateChanged 事件,以确定 Call Agent 是否从“已连接”状态更改为“已断开连接”状态。

const connectionState = callAgentInstance.connectionState;
console.log(connectionState); // it may return either of 'Connected' | 'Disconnected'

const connectionStateCallback = (args) => {
    console.log(args); // it will return an object with oldState and newState, each of having a value of either of 'Connected' | 'Disconnected'
    // it will also return reason, either of 'invalidToken' | 'connectionIssue'
}
callAgentInstance.on('connectionStateChanged', connectionStateCallback);

拨打电话

要创建并启动通话,请在callAgent上使用其中一个 API,并提供你通过通信服务标识 SDK 创建的用户。

创建和发起呼叫的操作是同步的。 可以通过 call 实例订阅通话事件。

向用户或 PSTN 发起一对 N 通话

若要呼叫另一个通信服务用户,请在 startCall 上使用 callAgent 方法,并传递你CommunicationUserIdentifier接收人的

对于对用户的 1:1 调用,请使用以下代码:

const userCallee = { communicationUserId: '<ACS_USER_ID>' }
const oneToOneCall = callAgent.startCall([userCallee]);

若要向公共交换电话网络 (PSTN) 发起通话,请在 startCall 上使用 callAgent 方法并传递接收方的 PhoneNumberIdentifier。 必须将通信服务资源配置为允许 PSTN 通话。

呼叫 PSTN 号码时,需要指定备用呼叫方 ID。 备用呼叫方 ID 是 PSTN 通话中用于标识呼叫方的电话号码(基于 E.164 标准)。 这是通话接收方看到的来电的电话号码。

注意

请参阅 PSTN 呼叫产品/服务的详细信息。 若要进行预览计划访问,请申请早期采用者计划

对于与 PSTN 号码之间的一对一通话,请使用以下代码:

const pstnCallee = { phoneNumber: '<ACS_USER_ID>' }
const alternateCallerId = {phoneNumber: '<ALTERNATE_CALLER_ID>'};
const oneToOneCall = callAgent.startCall([pstnCallee], { alternateCallerId });

对于与用户和 PSTN 号码的 1:n 通话,请使用以下代码:

const userCallee = { communicationUserId: '<ACS_USER_ID>' }
const pstnCallee = { phoneNumber: '<PHONE_NUMBER>'};
const alternateCallerId = {phoneNumber: '<ALTERNATE_CALLER_ID>'};
const groupCall = callAgent.startCall([userCallee, pstnCallee], { alternateCallerId });

加入会议室通话

若要联接 room 调用,可以实例化一个上下文对象,并以 roomId 属性作为 room 标识符。 若要加入通话,请使用 join 方法并传递上下文实例。

const context = { roomId: '<RoomId>' }
const call = callAgent.join(context);

通过 room,应用程序开发人员可以更好地控制谁可以加入通话、何时见面以及如何协作。 有关会议室的详细信息,请参阅 用于结构化会议的会议室 API加入会议室通话

加入群组通话

注意

该参数 groupId 是系统元数据,由微软用于执行运行系统所需的作业。 不要在 groupId 值中包含个人数据。 Microsoft 不会将此参数视为个人数据,其内容可能会显示给 Microsoft 员工,也可能会被长期存储。

groupId 参数要求数据采用 GUID 格式。 建议使用随机生成的 GUID,这些 GUID 在系统中不会被视为个人数据。

若要发起新的群组通话或加入正在进行的群组通话,请使用 join 方法并传递带有 groupId 属性的对象。 groupId 值必须是 GUID。

const context = { groupId: '<GUID>'};
const call = callAgent.join(context);

接听来电

当登录的身份收到来电时,callAgent 实例会触发 incomingCall 事件。 若要收听此事件,请使用以下选项之一进行订阅:

const incomingCallHandler = async (args: { incomingCall: IncomingCall }) => {
    const incomingCall = args.incomingCall;

    // Get incoming call ID
    var incomingCallId = incomingCall.id

    // Get information about this Call. This API is provided as a preview for developers
    // and may change based on feedback that we receive. Do not use this API in a production environment.
    // To use this api please use 'beta' release of Azure Communication Services Calling Web SDK
    var callInfo = incomingCall.info;

    // Get information about caller
    var callerInfo = incomingCall.callerInfo

    // Accept the call
    var call = await incomingCall.accept();

    // Reject the call
    incomingCall.reject();

    // Subscribe to callEnded event and get the call end reason
     incomingCall.on('callEnded', args => {
        console.log(args.callEndReason);
    });

    // callEndReason is also a property of IncomingCall
    var callEndReason = incomingCall.callEndReason;
};
callAgentInstance.on('incomingCall', incomingCallHandler);

incomingCall 事件包括你可以接受或拒绝的一个 incomingCall 实例。

如果在启动、接受或加入启用了视频功能的通话时摄像头不可用,Azure Communication Calling SDK 会引发 cameraStartFailed: true 通话诊断。 在这种情况下,通话开始时关闭了视频。 相机可能不可用,因为它正被另一个进程使用或在作系统中被禁用。

搁置和恢复通话

注意

在任何给定时刻,只有一个 (1) 活动通话处于 Connected 状态,同时有活动媒体。 所有其他通话必须由用户或通过应用程序编程方式暂停。 这种情况很常见,例如联系中心,用户可能需要处理多个出站和入站呼叫。 在这种情况下,应挂起所有非活动通话,并且用户应仅在活动通话中与其他人交互。

若要保留或恢复调用,请使用 holdresume 异步 API:

若要搁置通话:

await call.hold();

hold 操作解析时,调用状态设置为 LocalHold。 在 1:1 通话中,另一位参与者也被置于挂起状态,并且从该参与者的角度来看,通话状态被设置为 RemoteHold。 稍后,另一个参与者可能会将其通话挂起,这将导致状态变为 LocalHold

在群组通话或会议中,hold 是本地操作,不会为其他通话参与者保持通话。

若要恢复通话,所有发起暂停的用户都必须恢复。

若要从搁置恢复通话:

await call.resume();

resume操作解析完成时,调用状态再次被设置为 Connected

对通话进行静音和取消静音

若要将本地终结点静音或取消静音,请使用 muteunmute 异步 API:

//mute local device (microphone / sent audio)
await call.mute();

//unmute local device (microphone / sent audio)
await call.unmute();

将传入的音频设置为静音和取消静音

如果将传入的音频设置为静音,这会将通话音量设置为 0。 若要静音或取消静音传入音频,请使用 muteIncomingAudiounmuteIncomingAudio 异步操作。

//mute local device (speaker)
await call.muteIncomingAudio();

//unmute local device (speaker)
await call.unmuteIncomingAudio();

当传入的音频被静音时,参与者客户端 SDK 仍将收到呼叫音频(远程参与者的音频)。 调用“call.unmuteIncomingAudio()”后,才会在扬声器中听到通话音频,参与者才能听到。 但是,我们可以对呼叫音频应用筛选器并播放筛选的音频。

管理远程参与者

所有远程参与者都被包含在RemoteParticipant对象中,并可通过调用实例上的remoteParticipants集合获得。 对象 remoteParticipants 可从 Call 实例访问。

列出通话参与者

remoteParticipants 集合返回通话中远程参与者的列表:

call.remoteParticipants; // [remoteParticipant, remoteParticipant....]

向通话添加参与者

若要将参与者(用户或电话号码)添加到呼叫,请使用addParticipant 操作。 选择 Identifier 类型之一。 它将同步返回 remoteParticipant 实例。 当参与者成功添加到通话中时,它会在通话中引发 remoteParticipantsUpdated 事件。

const userIdentifier = { communicationUserId: '<ACS_USER_ID>' };
const pstnIdentifier = { phoneNumber: '<PHONE_NUMBER>' }
const remoteParticipant = call.addParticipant(userIdentifier);
const alternateCallerId = {  phoneNumber: '<ALTERNATE_CALLER_ID>' };
const remoteParticipant = call.addParticipant(pstnIdentifier, { alternateCallerId });

删除通话参与者

若要从呼叫中删除参与者(用户或电话号码),可以呼叫 removeParticipant。 你必须传递 Identifier 类型之一。 此方法会在将参与者从呼叫中移除后异步完成。 还将从 remoteParticipants 集合中删除该参与者。

const userIdentifier = { communicationUserId: '<ACS_USER_ID>' };
const pstnIdentifier = { phoneNumber: '<PHONE_NUMBER>' }
await call.removeParticipant(userIdentifier);
await call.removeParticipant(pstnIdentifier);

访问远程参与者属性

远程参与者有一组关联的属性和集合:

  • CommunicationIdentifier:获取远程参与者的标识符。 标识是 CommunicationIdentifier 类型之一:

    const identifier = remoteParticipant.identifier;
    
  • 它可能是下列 CommunicationIdentifier 类型之一:

    • { communicationUserId: '<ACS_USER_ID'> }:表示 Azure 通信服务用户的对象。
    • { phoneNumber: '<E.164>' }:一个对象,表示采用 E.164 格式的电话号码。
    • { microsoftTeamsUserId: '<TEAMS_USER_ID>', isAnonymous?: boolean; cloud?: "public" | "dod" | "gcch" }:一个对象,表示 Teams 用户。
    • { id: string }:表示不属于其他类型的标识符的对象
  • state:获取远程参与者的状态。

    const state = remoteParticipant.state;
    
  • 此状态可能是:

    • Idle:初始状态。
    • Connecting:参与者正在连接到通话时的过渡状态。
    • Ringing:参与者电话正在响铃。
    • Connected:参与者已连接到通话。
    • Hold:参与者已暂停通话。
    • EarlyMedia:在参与者连接到通话之前播放的通知。
    • InLobby:指示远程参与者位于会议厅中。
    • Disconnected:最终状态。 参与者已断开通话连接。 如果远程参与者断开了其网络连接,则两分钟后其状态将变为 Disconnected
  • callEndReason:若要了解参与者退出通话的原因,请检查 callEndReason 属性:

    const callEndReason = remoteParticipant.callEndReason;
    const callEndReasonCode = callEndReason.code // (number) code associated with the reason
    const callEndReasonSubCode = callEndReason.subCode // (number) subCode associated with the reason
    

    注意

    仅当通过 Call.addParticipant() API 添加远程参与者且远程参与者拒绝时,才会设置此属性。

    在场景中,UserB 将 UserC 移出,从 UserA 的角度来看,UserA 并没有看到这个标志在 UserC 上被设置。 换句话说,UserA 根本看不到 UserC 的 callEndReason 属性被设置。

  • isMuted 状态:若要了解远程参与者是否已静音,请检查 isMuted 属性。 它将返回 Boolean

    const isMuted = remoteParticipant.isMuted;
    
  • isSpeaking 状态:若要了解远程参与者是否正在讲话,请检查 isSpeaking 属性。 它将返回 Boolean

    const isSpeaking = remoteParticipant.isSpeaking;
    
  • videoStreams:若要检查给定参与者在此通话中发送的所有视频流,请检查 videoStreams 集合。 它包含 RemoteVideoStream 对象。

    const videoStreams = remoteParticipant.videoStreams; // [RemoteVideoStream, ...]
    
  • displayName:若要获取此远程参与者的显示名称,请检查 displayName 属性。它将返回字符串。

    const displayName = remoteParticipant.displayName;
    
  • endpointDetails:获取此远程参与者的所有终结点的详细信息

    const endpointDetails: EndpointDetails[] = remoteParticipant.endpointDetails;
    

    注意

    远程参与者可能通过许多不同的终结点加入通话,并且每个终结点都有其唯一的participantIdparticipantId 不同于 RemoteParticipant 标识符的原始 ID。

将其他参与者静音

注意

使用 Azure 通信服务呼叫 Web SDK 版本 1.26.1 或更高版本。

若要将连接到通话的所有其他参与者或特定参与者静音,可对通话使用异步 API muteAllRemoteParticipants,对远程参与者使用 mute。 当本地参与者被其他人静音时,将引发通话中的 mutedByOthers 事件。

重要

Azure 通信服务的这一功能目前以预览版提供。 预览版中的功能已公开发布,可供所有新客户和现有Microsoft客户使用。

预览版 API 和 SDK 在没有服务级别协议的情况下提供。 建议不要将它们用于生产工作负荷。 某些功能可能不受支持,或者功能可能受到限制。

有关详细信息,请参阅 Microsoft Azure 预览版补充使用条款

使用呼叫 WebJS SDK 使 PSTN 终结点静音功能目前处于公共预览阶段,可在版本 1.34.1 1.34.1 及更高版本中使用。

注意

不支持在一对一通话中静音其他人。

//mute all participants except yourself
await call.muteAllRemoteParticipants();

//mute a specific participant
await call.remoteParticipants[0].mute();

检查通话属性

获取通话的唯一 ID(字符串):

const callId: string = call.id;

获取本地参与者 ID:

const participantId: string = call.info.participantId;

注意

Azure 通信服务标识可以使用许多终结点中的 Web 通话 SDK,每个终结点都有自己的唯一 participantIdparticipantId 不同于 Azure 通信服务标识原始 ID。

如果您要加入 Teams 会议,请获取线程 ID。

const threadId: string | undefined = call.info.threadId;

获取通话信息:

const callInfo = call.info;

通过检查 remoteParticipants 实例上的 call 集合,了解通话中的其他参与者:

const remoteParticipants = call.remoteParticipants;

识别来电的呼叫方:

const callerIdentity = call.callerInfo.identifier;

identifierCommunicationIdentifier 类型之一。

获取通话状态:

const callState = call.state;

这会返回一个表示当前通话状态的字符串:

  • None:初始通话状态。
  • Connecting:拨打或接听电话后的初始过渡状态。
  • Ringing:对于去电,表示远程参与者的电话正在响铃。 在远程参与者端,它是 Incoming
  • EarlyMedia:表示在接通电话前播放通知的状态。
  • Connected:指示通话已连接。
  • LocalHold:表示通话中的本地参与者已将通话置于保持状态。 本地终结点与远程参与者之间没有媒体流动。
  • RemoteHold:表示通话中的远程参与者已将通话置于保持状态。 本地终结点与远程参与者之间没有媒体流动。
  • InLobby:指示用户位于会议厅中。
  • Disconnecting:在通话进入 Disconnected 状态之前的过渡状态。
  • Disconnected:最终通话状态。 如果网络连接断开,则两分钟后状态将变为 Disconnected

检查 callEndReason 属性来查明通话结束的原因:

const callEndReason = call.callEndReason;
const callEndReasonMessage = callEndReason.message // (string) user friendly message
const callEndReasonCode = callEndReason.code // (number) code associated with the reason
const callEndReasonSubCode = callEndReason.subCode // (number) subCode associated with the reason

通过检查 direction 属性了解当前通话是来电还是去电。 它将返回 CallDirection

const isIncoming = call.direction == 'Incoming';
const isOutgoing = call.direction == 'Outgoing';

通过检查 localVideoStreams 集合来检查活动视频流和活动屏幕共享流。 该localVideoStreams操作返回类型为LocalVideoStreamVideoScreenSharingRawMedia对象。

const localVideoStreams = call.localVideoStreams;

检查当前麦克风是否已静音。 它将返回 Boolean

const muted = call.isMuted;

检查当前传入的音频(扬声器)是否被静音。 它将返回 Boolean

const incomingAudioMuted = call.isIncomingAudioMuted;

检查视频是否打开。 它将返回 Boolean

const isLocalVideoStarted = call.isLocalVideoStarted;

检查屏幕共享是否打开。 它将返回 Boolean

const isScreenSharingOn = call.isScreenSharingOn;

挂断

有两种方法可以挂断呼叫。

  • 初始呼叫者可以离开呼叫,其他参与者将保留在呼叫中。
  • 当初始呼叫者离开时,通话将结束,所有参与者将断开连接。

若要退出呼叫,请使用:

call.hangUp();

提供 HangUpOptions以结束所有参与者的通话。

注意

此操作在会议室中不可用。

call.hangUp( forEveryone: true);

安装 SDK

找到项目级 build.gradle 文件,并将 mavenCentral() 添加到 buildscriptallprojects 下的存储库列表中:

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();

拨打电话

若要创建和启动呼叫,需要调用 CallAgent.startCall() 方法并提供一个或多个收件人的 Identifier

若要加入组呼叫,需要调用CallAgent.join()方法并提供groupId。 组 ID 必须采用 GUID 或 UUID 格式。

创建和发起呼叫的操作是同步的。 呼叫实例允许你订阅所有与呼叫相关的事件。

向用户发出一对一呼叫

若要向另一位通信服务用户发出呼叫,请对 call 调用 callAgent 方法,并传递带有 communicationUserId 键的对象。

StartCallOptions startCallOptions = new StartCallOptions();
Context appContext = this.getApplicationContext();
CommunicationUserIdentifier acsUserId = new CommunicationUserIdentifier(<USER_ID>);
CommunicationUserIdentifier participants[] = new CommunicationUserIdentifier[]{ acsUserId };
call oneToOneCall = callAgent.startCall(appContext, participants, startCallOptions);

向用户和 PSTN 发起一对多通话

注意

请参阅 PSTN 呼叫产品/服务的详细信息。 若要进行预览计划访问,请申请早期采用者计划

若要向用户发出 1:n 呼叫和公用电话交换网 (PSTN) 号码,需要指定接收方的电话号码。

通信服务资源必须配置为启用 PSTN 呼叫:

CommunicationUserIdentifier acsUser1 = new CommunicationUserIdentifier(<USER_ID>);
PhoneNumberIdentifier acsUser2 = new PhoneNumberIdentifier("<PHONE_NUMBER>");
CommunicationIdentifier participants[] = new CommunicationIdentifier[]{ acsUser1, acsUser2 };
StartCallOptions startCallOptions = new StartCallOptions();
Context appContext = this.getApplicationContext();
Call groupCall = callAgent.startCall(participants, startCallOptions);

接受呼叫

若要接受来电,请对呼叫对象调用 accept 方法。

Context appContext = this.getApplicationContext();
IncomingCall incomingCall = retrieveIncomingCall();
Call call = incomingCall.accept(context).get();

若要接受启用了摄像机的呼叫:

Context appContext = this.getApplicationContext();
IncomingCall incomingCall = retrieveIncomingCall();
AcceptCallOptions acceptCallOptions = new AcceptCallOptions();
VideoDeviceInfo desiredCamera = callClient.getDeviceManager().get().getCameraList().get(0);
acceptCallOptions.setVideoOptions(new VideoOptions(new LocalVideoStream(desiredCamera, appContext)));
Call call = incomingCall.accept(context, acceptCallOptions).get();

通过订阅 callAgent 对象上的 onIncomingCall 事件来获取传入呼叫:

// Assuming "callAgent" is an instance property obtained by calling the 'createCallAgent' method on CallClient instance 
public Call retrieveIncomingCall() {
    IncomingCall incomingCall;
    callAgent.addOnIncomingCallListener(new IncomingCallListener() {
        void onIncomingCall(IncomingCall inboundCall) {
            // Look for incoming call
            incomingCall = inboundCall;
        }
    });
    return incomingCall;
}

加入会议室通话

使用 CallAgentRoomCallLocator,通过指定 roomId 来加入聊天室通话。 该方法 CallAgent.join 返回一个 Call 对象:

val roomCallLocator = RoomCallLocator(roomId)
call = callAgent.join(applicationContext, roomCallLocator, joinCallOptions)

通过 room,应用程序开发人员可以更好地控制谁可以加入通话、何时见面以及如何协作。 有关会议室的详细信息,请参阅 结构化会议的会议室 API加入会议室通话

加入群组通话

若要启动新的组调用或加入正在进行的组调用,需要调用 join 该方法并传递具有 groupId 属性的对象。 该值必须为 GUID。

Context appContext = this.getApplicationContext();
GroupCallLocator groupCallLocator = new GroupCallLocator("<GUID>");
JoinCallOptions joinCallOptions = new JoinCallOptions();

call = callAgent.join(context, groupCallLocator, joinCallOptions);

通话属性

获取此呼叫的唯一 ID:

String callId = call.getId();

若要了解呼叫中的其他参与者,请检查 remoteParticipant 实例上的 call 集合:

List<RemoteParticipant> remoteParticipants = call.getRemoteParticipants();

主叫方的标识(如果是来电):

CommunicationIdentifier callerId = call.getCallerInfo().getIdentifier();

获取呼叫的状态:

CallState callState = call.getState();

它会返回一个表示当前呼叫状态的字符串:

  • NONE - 初始调用状态
  • EARLY_MEDIA - 表示在接通电话前播放通知的状态
  • CONNECTING - 发出或接受调用后的初始转换状态
  • RINGING - 对于传出呼叫 - 表示远程参与者的电话正在响铃
  • CONNECTED - 通话已连接
  • LOCAL_HOLD - 呼叫被本地参与者挂起,本地终端和远程参与者之间没有媒体流动
  • REMOTE_HOLD - 由远程参与者搁置的呼叫,本地终结点和远程参与者之间没有媒体流动
  • DISCONNECTING - 调用进入 Disconnected 状态前的转换状态
  • DISCONNECTED - 最终调用状态
  • IN_LOBBY - 在大厅中,为了实现 Teams 会议互操作性

若要了解呼叫结束的原因,请检查 callEndReason 属性。 它包含代码/子代码:

CallEndReason callEndReason = call.getCallEndReason();
int code = callEndReason.getCode();
int subCode = callEndReason.getSubCode();

若要查看当前呼叫是来电还是去电,请检查 callDirection 属性:

CallDirection callDirection = call.getCallDirection(); 
// callDirection == CallDirection.INCOMING for incoming call
// callDirection == CallDirection.OUTGOING for outgoing call

若要查看当前麦克风是否静音,请检查 muted 属性:

boolean muted = call.isMuted();

若要检查活动视频流,请查看 localVideoStreams 集合:

List<LocalVideoStream> localVideoStreams = call.getLocalVideoStreams();

静音和取消静音

若要使本地终结点静音或取消静音,可使用 muteunmute 异步 API:

Context appContext = this.getApplicationContext();
call.mute(appContext).get();
call.unmute(appContext).get();

更改通话音量

当参与者处于呼叫状态时,手机上的硬件音量密钥应允许用户更改呼叫音量。

在发生通话的活动上使用流类型为 AudioManager.STREAM_VOICE_CALL 的方法 setVolumeControlStream

此方法使硬件音量键能够更改通话音量,在音量滑块上用电话图标或类似的标志表示。 它还可防止其他声音配置(如警报、媒体或系统范围音量)更改音量。 有关详细信息,请参阅 处理音频输出中的更改 |Android 开发人员

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}

远程参与者管理

所有远程参与者都具有 RemoteParticipant 类型,并且可以通过调用实例上的 remoteParticipants 集合访问。

列出通话参与者

remoteParticipants 集合会返回给定通话中远程参与者的列表:

List<RemoteParticipant> remoteParticipants = call.getRemoteParticipants(); // [remoteParticipant, remoteParticipant....]

向通话添加参与者

若要将用户或电话号码作为参与者添加到通话中,可以调用 addParticipant 操作。

此操作同步返回一个远程参与者实例。

const acsUser = new CommunicationUserIdentifier("<acs user id>");
const acsPhone = new PhoneNumberIdentifier("<phone number>");
RemoteParticipant remoteParticipant1 = call.addParticipant(acsUser);
AddPhoneNumberOptions addPhoneNumberOptions = new AddPhoneNumberOptions(new PhoneNumberIdentifier("<alternate phone number>"));
RemoteParticipant remoteParticipant2 = call.addParticipant(acsPhone, addPhoneNumberOptions);

删除通话参与者

若要从呼叫中删除参与者(用户或电话号码),可以调用 removeParticipant 操作。

从呼叫中删除参与者后,此操作将异步地完成。

还将从 remoteParticipants 集合中移除该参与者。

RemoteParticipant acsUserRemoteParticipant = call.getParticipants().get(0);
RemoteParticipant acsPhoneRemoteParticipant = call.getParticipants().get(1);
call.removeParticipant(acsUserRemoteParticipant).get();
call.removeParticipant(acsPhoneRemoteParticipant).get();

远程参与者属性

任何给定的远程参与者都有一组关联的属性和集合:

  • 获取此远程参与者的标识符。

标识是 Identifier 类型之一:

CommunicationIdentifier participantIdentifier = remoteParticipant.getIdentifier();
  • 获取此远程参与者的状态。

    ParticipantState state = remoteParticipant.getState();
    

状态可以是下列其中一项:

  • IDLE - 初始状态

  • EARLY_MEDIA - 在参与者连接到呼叫之前播放公告

  • RINGING - 参与者电话正在响铃

  • CONNECTING - 参与者连接到呼叫时的转换状态

  • CONNECTED - 参与者已连接到通话

  • HOLD - 参与者处于等待状态

  • IN_LOBBY - 参与者正在大厅中等待被允许进入。 当前仅在 Microsoft Teams 互操作方案中使用

  • DISCONNECTED - 最终状态 - 参与者已与通话断开连接

  • 若要了解参与者退出通话的原因,请检查 callEndReason 属性:

    CallEndReason callEndReason = remoteParticipant.getCallEndReason();
    
  • 若要检查此远程参与者是否已静音,请检查 isMuted 属性:

    boolean isParticipantMuted = remoteParticipant.isMuted();
    
  • 若要检查此远程参与者是否正在讲话,请检查 isSpeaking 属性:

    boolean isParticipantSpeaking = remoteParticipant.isSpeaking();
    
  • 若要检查给定参与者在此呼叫中发送的所有视频流,请检查 videoStreams 集合:

    List<RemoteVideoStream> videoStreams = remoteParticipant.getVideoStreams(); // [RemoteVideoStream, RemoteVideoStream, ...]
    

将其他参与者静音

注意

使用 Azure 通信服务调用 Android SDK 2.11.0 或更高版本。

当 PSTN 参与者静音时,他们会收到一个通知,指出他们已静音,他们可以按组合键(如 *6)取消静音。 当他们按 *6 时,将被取消静音。

若要在通话中静音所有其他参与者,请使用 muteAllRemoteParticipants API。

call.muteAllRemoteParticipants();

若要静音特定远程参与者,请对该远程参与者使用 API mute 操作。

remoteParticipant.mute();

若要通知本地参与者他们已被其他人静音,请订阅 onMutedByOthers 事件。

使用前台服务

如果要运行用户可见的任务,即使应用程序处于后台,也可以使用 Foreground Services

例如,使用 Foreground Services 在应用程序具有活动调用时向用户提供可见通知。 这样,即使用户转到主屏幕或从 最近屏幕中删除应用程序,呼叫仍会保持活动状态。

如果在通话中没有使用前台服务,则导航到主屏幕时,可保持通话,但如果从最近的屏幕中删除了应用程序且 Android OS 终止了应用程序进程,则会停止通话。

用户启动或加入调用时,应启动前台服务,例如:

call = callAgent.startCall(context, participants, options);
startService(yourForegroundServiceIntent);

挂断通话或通话状态为“已断开连接”时,应同时停止前台服务,例如:

call.hangUp(new HangUpOptions()).get();
stopService(yourForegroundServiceIntent);

前台服务详细信息

请记住,从最近的列表中移除应用时停止已运行的前台服务等方案会移除用户可见通知。 在这种情况下,Android OS 可以将应用程序进程保持活动状态一段时间,这意味着此时间段内调用可以保持活动状态。

例如,如果应用程序正在停止服务 onTaskRemoved 方法上的前台服务,则应用程序可以根据 活动生命周期启动或停止音频和视频。 例如,使用 onDestroy 方法替代销毁活动时停止音频和视频。

设置系统

按照以下步骤设置系统。

创建 Xcode 项目

在 Xcode 中,创建新的 iOS 项目,并选择“单视图应用”模板。 本文使用 SwiftUI 框架,因此应将“语言”设置为“Swift”,并将“界面”设置为“SwiftUI”

在本文中,无需创建测试。 请随意清除“包括测试”复选框。

显示用于在 Xcode 中创建项目的窗口的屏幕截图。

使用 CocoaPods 安装包和依赖项

  1. 为应用程序创建 Podfile,如此示例所示:

    platform :ios, '13.0'
    use_frameworks!
    target 'AzureCommunicationCallingSample' do
        pod 'AzureCommunicationCalling', '~> 1.0.0'
    end
    
  2. 运行 pod install

  3. 使用 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")
        }
})

注意

应用程序在实现事件委托时,必须持有对需要事件订阅的对象的强引用。 例如,调用 call.addParticipant 该方法并返回对象 RemoteParticipant 时。 然后,应用程序设置要侦听 RemoteParticipantDelegate 的委托,而应用程序必须保留对 RemoteParticipant 对象的强引用。 否则,如果收集了此对象,则当通话 SDK 尝试调用对象时,委托将引发严重异常。

拨出电话

若要创建并发起通话,需要在 CallAgent 上调用其中一个 API,并提供已使用通信服务管理 SDK 预配的用户的通信服务标识。

创建和发起呼叫的操作是同步的。 你会收到一个呼叫实例,使你能够订阅呼叫中的所有事件。

向用户发起一对一通话,或向用户和 PSTN 发起一对多通话

let callees = [CommunicationUser(identifier: 'UserId')]
self.callAgent?.startCall(participants: callees, options: StartCallOptions()) { (call, error) in
     if error == nil {
         print("Successfully started outgoing call")
         self.call = call
     } else {
         print("Failed to start outgoing call")
     }
}

向用户和 PSTN 发起一对多通话

注意

请参阅 PSTN 呼叫产品/服务的详细信息。 若要进行预览计划访问,请申请早期采用者计划

若要向用户和公用电话交换网(PSTN)拨打 1:n 呼叫,需要指定通过通信服务获取的电话号码。

let pstnCallee = PhoneNumberIdentifier(phoneNumber: '+1999999999')
let callee = CommunicationUserIdentifier('UserId')
self.callAgent?.startCall(participants: [pstnCallee, callee], options: StartCallOptions()) { (groupCall, error) in
     if error == nil {
         print("Successfully started outgoing call to multiple participants")
         self.call = groupCall
     } else {
         print("Failed to start outgoing call to multiple participants")
     }
}

加入会议室通话

若要加入 room 通话,请将 roomId 属性指定为 room 标识符。 若要加入通话,请使用 join 方法并传递 roomCallLocator

func joinRoomCall() {
    if self.callAgent == nil {
        print("CallAgent not initialized")
        return
    }
    
    if (self.roomId.isEmpty) {
        print("Room ID not set")
        return
    }
    
    // Join a call with a Room ID
    let options = JoinCallOptions()
    let audioOptions = AudioOptions()
    audioOptions.muted = self.muted
    
    options.audioOptions = audioOptions
    
    let roomCallLocator = RoomCallLocator(roomId: roomId)
    self.callAgent!.join(with: roomCallLocator, joinCallOptions: options) { (call, error) in
        self.setCallAndObserver(call: call, error: error)
    }
}

通过 room,应用程序开发人员可以更好地控制谁可以加入通话、何时见面以及如何协作。 有关会议室的详细信息,请参阅 结构化会议的会议室 API加入会议室通话

加入群组通话

若要加入通话,需要在 CallAgent 上调用其中一个 API。

let groupCallLocator = GroupCallLocator(groupId: UUID(uuidString: "uuid_string")!)
self.callAgent?.join(with: groupCallLocator, joinCallOptions: JoinCallOptions()) { (call, error) in
    if error == nil {
        print("Successfully joined group call")
        self.call = call
    } else {
        print("Failed to join group call")
    }
}

订阅接收来电通知

订阅来电事件。

final class IncomingCallHandler: NSObject, CallAgentDelegate, IncomingCallDelegate
{
    // Event raised when there is an incoming call
    public func callAgent(_ callAgent: CallAgent, didReceiveIncomingCall incomingcall: IncomingCall) {
        self.incomingCall = incomingcall
        // Subscribe to get OnCallEnded event
        self.incomingCall?.delegate = self
    }

    // Event raised when incoming call was not answered
    public func incomingCall(_ incomingCall: IncomingCall, didEnd args: PropertyChangedEventArgs) {
        print("Incoming call was not answered")
        self.incomingCall = nil
    }
}

接听来电

若要接听来电,请对 accept 对象调用 IncomingCall 方法。

self.incomingCall!.accept(options: AcceptCallOptions()) { (call, error) in
   if (error == nil) {
       print("Successfully accepted incoming call")
       self.call = call
   } else {
       print("Failed to accept incoming call")
   }
}

let firstCamera: VideoDeviceInfo? = self.deviceManager!.cameras.first
localVideoStreams = [LocalVideoStream]()
localVideoStreams!.append(LocalVideoStream(camera: firstCamera!))
let acceptCallOptions = AcceptCallOptions()
acceptCallOptions.videoOptions = VideoOptions(localVideoStreams: localVideoStreams!)
if let incomingCall = self.incomingCall {
    incomingCall.accept(options: acceptCallOptions) { (call, error) in
        if error == nil {
            print("Incoming call accepted")
        } else {
            print("Failed to accept incoming call")
        }
    }
} else {
  print("No incoming call found to accept")
}

执行通话中操作

你可以在通话期间执行操作,以管理与视频和音频相关的设置。

静音和取消静音

若要将本地终结点静音或取消静音,可使用 muteunmute 异步 API。

call!.mute { (error) in
    if error == nil {
        print("Successfully muted")
    } else {
        print("Failed to mute")
    }
}

使用以下代码以异步方式将本地终结点取消静音。

call!.unmute { (error) in
    if error == nil {
        print("Successfully un-muted")
    } else {
        print("Failed to unmute")
    }
}

管理远程参与者

RemoteParticipant 类型表示所有远程参与者。 它们可通过通话实例上的 remoteParticipants 集合获得。

列出通话参与者

call.remoteParticipants

向通话添加参与者

若要将参与者作为用户或电话号码添加到呼叫,请调用 addParticipant 操作。 此操作将同步返回远程参与者实例。

let remoteParticipantAdded: RemoteParticipant = call.add(participant: CommunicationUserIdentifier(identifier: "userId"))

删除通话参与者

若要以用户或电话号码的身份将参与者从通话中移除,请调用removeParticipant操作。 此操作以异步方式解析。

call!.remove(participant: remoteParticipantAdded) { (error) in
    if (error == nil) {
        print("Successfully removed participant")
    } else {
        print("Failed to remove participant")
    }
}

获取远程参与者属性

// [RemoteParticipantDelegate] delegate - an object you provide to receive events from this RemoteParticipant instance
var remoteParticipantDelegate = remoteParticipant.delegate

// [CommunicationIdentifier] identity - same as the one used to provision a token for another user
var identity = remoteParticipant.identifier

// ParticipantStateIdle = 0, ParticipantStateEarlyMedia = 1, ParticipantStateConnecting = 2, ParticipantStateConnected = 3, ParticipantStateOnHold = 4, ParticipantStateInLobby = 5, ParticipantStateDisconnected = 6
var state = remoteParticipant.state

// [Error] callEndReason - reason why participant left the call, contains code/subcode/message
var callEndReason = remoteParticipant.callEndReason

// [Bool] isMuted - indicating if participant is muted
var isMuted = remoteParticipant.isMuted

// [Bool] isSpeaking - indicating if participant is currently speaking
var isSpeaking = remoteParticipant.isSpeaking

// RemoteVideoStream[] - collection of video streams this participants has
var videoStreams = remoteParticipant.videoStreams // [RemoteVideoStream, RemoteVideoStream, ...]

将其他参与者静音

注意

使用 Azure 通信服务调用 iOS SDK 2.13.0 或更高版本。

当 PSTN 参与者静音时,他们会收到一个通知,指出他们已静音,他们可以按组合键(如 *6)取消静音。 当他们按 *6时,将被取消静音。

若要在通话中将所有其他参与者设为静音,请在通话中使用muteAllRemoteParticipants操作。

call!.muteAllRemoteParticipants { (error) in
    if error == nil {
        print("Successfully muted all remote participants.")
    } else {
        print("Failed to mute remote participants.")
    }
}

若要将特定远程参与者设为静音,请对给定远程参与者使用 mute 操作。

remoteParticipant.mute { (error) in
    if error == nil {
        print("Successfully muted participant.")
    } else {
        print("Failed to mute participant.")
    }
}

若要通知本地参与者他们已被其他人静音,请订阅 onMutedByOthers 事件。

设置系统

按照以下步骤设置系统。

创建 Visual Studio 项目

对于通用 Windows 平台应用,请在 Visual Studio 2022 中创建新的“空白应用(通用 Windows)”项目。 输入项目名称后,可随意选择任何版本高于 10.0.17763.0 的 Windows SDK。

对于 WinUI 3 应用,请使用“已打包空白应用(桌面中的 WinUI 3)”模板创建新项目,以设置单页 WinUI 3 应用。 需要 Windows App SDK 版本 1.3 或更高版本。

使用 NuGet 包管理器安装包和依赖项

可通过 NuGet 包公开提供通话 SDK API 和库。

要查找、下载和安装通话 SDK NuGet 包,请执行以下操作:

  1. 选择“工具”“NuGet 包管理器”>“管理解决方案的 NuGet 包”,以打开 NuGet 包管理器。>
  2. 选择“浏览”,然后在搜索框中输入 Azure.Communication.Calling.WindowsClient
  3. 确保已选中“包括预发行版”复选框
  4. 选择 Azure.Communication.Calling.WindowsClient 包,然后选择 Azure.Communication.Calling.WindowsClient1.4.0-beta.1 或更新版本。
  5. 在右侧窗格中选中与 Azure 通信服务项目对应的复选框。
  6. 选择“安装” 。

在 Visual Studio 中实现示例应用

本部分介绍如何开发应用以管理在 Visual Studio 中工作的呼叫。

请求访问麦克风

应用需要访问麦克风。 在通用 Windows 平台(UWP)应用中,麦克风功能必须在应用清单文件中声明。

访问麦克风:

  1. Solution Explorer 面板中,双击扩展名为 .appxmanifest 的文件。
  2. 单击 Capabilities 选项卡。
  3. 从功能列表中选中 Microphone 复选框。

创建用于发起和挂起通话的 UI 按钮

此示例应用包含两个按钮。 一个用于发起通话,另一个用于挂起已发起的通话。

向应用添加两个按钮。

  1. Solution Explorer 面板中,对于 UWP,双击名为 MainPage.xaml 的文件;对于 WinUI 3,双击名为 MainWindows.xaml 的文件。
  2. 在中央面板中的 UI 预览下查找 XAML 代码。
  3. 修改 XAML 代码,摘录如下:
<TextBox x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" />
<StackPanel>
    <Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" />
    <Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" />
</StackPanel>

使用 Calling SDK 的 API 设置应用程序

通话 SDK API 在两个不同的命名空间中。

以下步骤告知 C# 编译器这些命名空间,使 Visual Studio 的 Intellisense 有助于进行代码开发。

  1. Solution Explorer 面板中,对于 UWP,单击名为 MainPage.xaml 的文件左侧的箭头;对于 WinUI 3,单击名为 MainWindows.xaml 的文件左侧的箭头。
  2. 双击名为 MainPage.xaml.csMainWindows.xaml.cs 的文件。
  3. 在当前 using 语句的底部添加以下命令。
using Azure.Communication.Calling.WindowsClient;

保持 MainPage.xaml.csMainWindows.xaml.cs 打开。 后续步骤将添加更多代码。

启用应用交互

先前添加的 UI 按钮需在已放置的 CommunicationCall 之上操作。 这意味着,CommunicationCall 数据成员应被添加到 MainPageMainWindow 类。 此外,若要使创建 CallAgent 的异步操作成功,还应将 CallAgent 数据成员添加到同一类。

将以下数据成员添加到 MainPageMainWindow 类:

CallAgent callAgent;
CommunicationCall call;

创建按钮处理程序

之前向 XAML 代码添加了两个 UI 按钮。 以下代码添加了在用户选择按钮时要执行的处理程序。 应在上一部分的数据成员之后添加以下代码。

private async void CallButton_Click(object sender, RoutedEventArgs e)
{
    // Start call
}

private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
    // End the current call
}

对象模型

以下类和接口处理适用于 UWP 的 Azure 通信服务通话客户端库的某些主要功能。

名称 说明
CallClient CallClient 是通话客户端库的主入口点。
CallAgent CallAgent 用于启动和加入通话。
CommunicationCall CommunicationCall 用于管理已发起或已加入的通话。
CommunicationTokenCredential CommunicationTokenCredential 用作实例化 CallAgent 的令牌凭据。
CallAgentOptions CallAgentOptions 包含用于标识呼叫方的信息。
HangupOptions HangupOptions 告知是否应终止其所有参与者的通话。

初始化 CallAgent

若要从 CallAgent 创建 CallClient 实例,必须使用 CallClient.CreateCallAgentAsync 方法,该方法在初始化后异步返回 CallAgent 对象。

若要创建 CallAgent,必须传递 CallTokenCredential 对象和 CallAgentOptions 对象。 请记住,如果传递了格式错误的令牌,则会引发 CallTokenCredential

若要在应用初始化期间调用,请在帮助程序函数中添加以下代码。

var callClient = new CallClient();
this.deviceManager = await callClient.GetDeviceManagerAsync();

var tokenCredential = new CallTokenCredential("<AUTHENTICATION_TOKEN>");
var callAgentOptions = new CallAgentOptions()
{
    DisplayName = "<DISPLAY_NAME>"
};

this.callAgent = await callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);

为你的资源将 <AUTHENTICATION_TOKEN> 更改为有效凭据令牌。 如果需要获取凭据令牌,请参阅 用户访问令牌

创建 CallAgent 并发起通话

用于创建 CallAgent 的对象现已准备就绪。 现在,可以异步创建 CallAgent 并发起通话。

在处理上一步中的异常后添加以下代码。

var startCallOptions = new StartCallOptions();
var callees = new [] { new UserCallIdentifier(CalleeTextBox.Text.Trim()) };

this.call = await this.callAgent.StartCallAsync(callees, startCallOptions);
this.call.OnStateChanged += Call_OnStateChangedAsync;

使用 8:echo123 来与 Azure 通信服务回显机器人进行交互。

静音和取消静音

若要将传出的音频静音或取消静音,请使用 MuteOutgoingAudioAsyncUnmuteOutgoingAudioAsync 异步操作:

// mute outgoing audio
await this.call.MuteOutgoingAudioAsync();

// unmute outgoing audio
await this.call.UnmuteOutgoingAudioAsync();

将其他参与者静音

注意

使用 Azure 通信服务调用 Windows SDK 版本 1.9.0 或更高版本。

当 PSTN 参与者静音时,他们必须收到通知,指出他们已被静音,并且他们可以按组合键(如 *6)来取消静音。 当他们按 *6 时,他们必须被取消静音。

若要将所有其他参与者静音或将特定参与者设为静音,请在通话上的MuteAllRemoteParticipantsAsync和远程参与者上的MuteAsync使用异步操作:

// mute all participants except yourself
await this.call.MuteAllRemoteParticipantsAsync();

// mute specific participant in the call
await this.call.RemoteParticipants.FirstOrDefault().MuteAsync();

若要通知本地参与者他们已被其他人静音,请订阅 MutedByOthers 事件。

结束呼叫

发起通话后,使用 CommunicationCall 对象的 HangupAsync 方法来挂断通话。

使用实例 HangupOptions 通知所有参与者是否必须终止呼叫。

在以下代码中添加 HangupButton_Click

this.call.OnStateChanged -= Call_OnStateChangedAsync;
await this.call.HangUpAsync(new HangUpOptions() { ForEveryone = false });

运行代码

  1. 确保 Visual Studio 为x64x86ARM64生成应用。
  2. F5 开始运行应用。
  3. 应用运行后,单击 “呼叫 ”按钮向定义的收件人发出呼叫。

首次运行应用时,系统会提示用户授予对麦克风的访问权限。

后续步骤