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

使用通信服务呼叫 SDK 管理 Teams 用户的呼叫

了解如何使用 Azure 通信服务 SDK 管理通话。 我们将了解如何发起通话,如何管理其参与者和属性。

先决条件

安装 SDK

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

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

初始化所需的对象

创建实例 CallClient 以启动呼叫堆栈。 可以使用实例 AzureLogger 和方法 setLogLevel 来配置呼叫 SDK 的日志记录。 可以使用方法 getDeviceManager 访问操作系统的 deviceManager

然后使用方法 createTeamsCallAgent 异步创建一个 TeamsCallAgent 实例,该实例将管理 Teams 用户的传入和传出呼叫。 该方法采用 CommunicationTokenCredential 作为表示 Teams 用户访问令牌的参数。

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 wherever desired. To console, file, buffer, REST API, etc...
AzureLogger.log = (...args) => {
    console.log(...args); // Redirect log output to console
};

const userToken = '<USER_TOKEN>';
callClient = new CallClient();
const tokenCredential = new AzureCommunicationTokenCredential(userToken);
const teamsCallAgent = await callClient.createTeamsCallAgent(tokenCredential);
const deviceManager = await callClient.getDeviceManager();

拨打电话

启动具有 teamsCallAgentstartCall API 的同步一对一或组呼叫。 可以提供 MicrosoftTeamsUserIdentifierPhoneNumberIdentifier 作为参数来定义呼叫的目标。 此方法返回可以订阅通话事件的 TeamsCall 实例。

注意

在调用 startCall 方法时,启动与 teamsCallAgent 的组呼叫需要聊天的 threadId。 创建的 TeamsCall 实例具有捕获此线程的属性 threadId。 通信服务呼叫 SDK 不会使参与者保持聊天和呼叫名单同步。Microsft 鼓励开发人员保持名单同步,以获得最佳用户体验。 了解如何管理聊天线程

启动一对一 IP 语音 (VoIP) 呼叫 Teams 用户:

const userCallee = { microsoftTeamsUserId: '<MICROSOFT_TEAMS_USER_ID>' };
const oneToOneCall = teamsCallAgent.startCall(userCallee);

启动一对一电话呼叫 E.164 电话号码:

const phoneCallee = { phoneNumber: '<PHONE_NUMBER_E164_FORMAT>' }
const oneToOneCall = teamsCallAgent.startCall(phoneCallee );

使用 IP 语音 (VoIP) 和电话号码启动对 Teams 用户的组呼叫:

const userCallee = { microsoftTeamsUserId: '<MICROSOFT_TEAMS_USER_ID>' }
const phoneCallee = { phoneNumber: '<PHONE_NUMBER_E164_FORMAT>'};
const groupCall = teamsCallAgent.startCall([userCallee, phoneCallee], { threadId: '<THREAD_ID>' });

加入通话

加入 Teams 会议

可以使用 teamsCallAgent 实例上的方法 join 加入 Teams 会议。 Teams 用户可以通过提供 TeamsMeetingLinkLocatorTeamsMeetingCoordinatesLocatorTeamsMeetingIdLocator 来加入 Teams 会议。

使用会议 URL 加入 Teams 会议:

const meetingCall = teamsCallAgent.join({ meetingLink: '<MEETING_LINK>' });

结合使用线程 ID、组织者 ID、租户 ID 和消息 ID 加入 Teams 会议:

const meetingCall = teamsCallAgent.join({ threadId: '<THREAD_ID>', organizerId: '<ORGANIZER_ID>', tenantId: '<TENANT_ID>', messageId: '<MESSAGE_ID>' });

使用会议代码和密码加入 Teams 会议:

const meetingCall = teamsCallAgent.join({ meetingId: '<MEETING_CODE>', passcode: '<PASSCODE>'});

使用会议 ID 和密码加入 Teams 会议:

开发人员可以通过多种方式将参与者连接到 Teams 会议。 一种方式是使用会议 ID 和密码,这样用户能够从设备或应用程序加入他们受邀加入的 Teams 会议。 始终需要提供会议 ID 和密码才能加入会议。 密码区分大小写。

  • 会议 ID 和密码的格式:

    • 会议 ID:12 位数字。
    • 密码:6 个字符
  • 需要多久刷新一次会议 ID 和密码?

    • 会议 ID 和密码一经创建,就不会更改。 开发人员无需刷新其中任何一项。
    • Teams 会议组织者无法重新生成会议 ID 和密码。
  • 用户通过 URL 或会议 ID 和密码加入的 Teams 会议体验有什么区别吗?

    • 没有,如果参与者使用 Teams 会议 URL 或会议 ID 和密码加入 Teams 会议,那么他们的体验是相同的。
  • 开发人员应如何存储和管理密码?

    • 会议 ID 和密码是加入会议的坐标。 开发人员应将密码视为机密,对其进行加密,如果要存储,则应确保其处于访问受控制的环境中。
    • 如果公开了坐标,任何人都可以加入会议,会破坏每个参会者的体验。
  • 如何获取会议 ID 和密码?

    1. 图形 API:使用图形 API 检索有关 onlineMeeting 资源的信息并检查属性 joinMeetingIdSettings 中的对象。
    2. Teams:在 Teams 应用程序中,转到“Calendar”应用并打开会议的详细信息。 在线会议的会议定义中有会议 ID 和密码。
    3. Outlook:可以在日历事件或电子邮件会议邀请中找到会议 ID 和密码。
    4. 开发人员无法通过调用 SDK 检索会议 ID 和密码,也无法从详细控制台日志中检索它。
  • 如何验证会议 ID 和密码是否正确?

接听 Teams 来电

可以订阅 incomingCall 实例上的 teamsCallAgent 事件,以向 Teams 用户注册传入呼叫。 该事件具有一个有 TeamsIncomingCall 实例的 teamsIncomingCall 属性,允许你 acceptreject 传入呼叫。

const incomingCallHandler = async (args: { teamsIncomingCall: TeamsIncomingCall }) => {
    const incomingCall = args.teamsIncomingCall;
    // Get Teams incoming call ID
    const 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
    const callInfo = incomingCall.info;
    // Get information about caller
    const callerInfo = incomingCall.callerInfo
    // Accept the call
    const teamsCall = 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;
};
teamsCallAgent.on('incomingCall', incomingCallHandler);

启用和禁用视频

可以从 TeamsCall 实例中的 localVideoStreams 属性获取本地视频流集合。 如果启用,则集合包含屏幕共享流和相机视频源。 可以通过检查属性 TeamsCall 来获取远程参与者的视频流。remoteParticipants 其中,每个参与者都有属性 videoStreams 中的视频流集合。

静音和取消静音

可以在 TeamsCall 实例上使用 muteunmute 异步 API,在本地对 Teams 用户进行静音或取消静音操作。 本地静音可防止把音频发送给其他参与者。

//mute local device
await call.mute();
//unmute local device
await call.unmute();

将其他参与者设为静音

若要将所有其他参与者静音或将特定参与者静音,可以对通话使用异步 API muteAllRemoteParticipants,对远程参与者使用 mute

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

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

注意

此 API 以预览状态提供给开发者,可能根据我们收到的反馈更改。 请勿在生产环境中使用此 API。 若要使用此 API,请使用 Azure 通信服务呼叫 Web SDK 的 beta 版本。

管理远程参与者

其他呼叫参与者可在属性 remoteParticipants 下的 TeamsCall 实例中使用。 这是 RemoteParticipant 对象的集合。 你可以列出、添加和删除通话中的其他参与者。

注意

添加参与者方法需要聊天的 threadId。 通信服务呼叫 SDK 不会使参与者保持聊天和呼叫名单同步。Microsft 鼓励开发人员保持名单同步,以获得最佳用户体验。 了解如何管理聊天线程

可以通过对对象 TeamsCall 调用方法 addParticipant 向 Teams 呼叫或 Teams 会议添加新的 Teams 用户或电话号码。 该方法接受标识符 MicrosoftTeamsUserIdentifierPhoneNumberIdentifier 作为输入,同步返回 TeamsCall 实例上的 RemoteParticipant 实例并触发事件 remoteParticipantsUpdated

可以通过异步调用 TeamsCall 实例上的 removeParticipant 方法,从 Teams 呼叫或 Teams 会议中删除参与者。 该方法接受标识符 MicrosoftTeamsUserIdentifierPhoneNumberIdentifier 作为输入。 从 remoteParticipants 集合中删除 RemoteParticipant 时,会解析该方法,并触发 TeamsCall 实例上的 remoteParticipantsUpdated 事件。

列出其他呼叫参与者:

const participants = call.remoteParticipants; // [remoteParticipant, remoteParticipant....]

将 Teams 用户和电话号码添加到 Teams 呼叫或 Teams 会议:

const teamsUser = { microsoftTeamsUserId: '<MICROSOFT_TEAMS_USER_ID>' };
const phoneUser = { phoneNumber: '<PHONE_NUMBER_E164_FORMAT>' }
const remoteParticipant = call.addParticipant(teamsUser , { threadId: '<THREAD_ID>' });
const remoteParticipant2 = call.addParticipant(phoneUser , { threadId: '<THREAD_ID>' });

将 Teams 用户和电话号码从 Teams 呼叫或 Teams 会议中删除:

const teamsUser = { microsoftTeamsUserId: '<MICROSOFT_TEAMS_USER_ID>' };
const phoneUser = { phoneNumber: '<PHONE_NUMBER_E164_FORMAT>' }
await call.removeParticipant(teamsUser);
await call.removeParticipant(phoneUser);

远程参与者

远程参与者表示已连接到正在进行的 Teams 呼叫或 Teams 会议的终结点。 类 remoteParticipant 具有以下一组属性和集合:

  • identifier:返回以下标识符之一:CommunicationUserIdentifierMicrosoftTeamsUserIdentifierPhoneNumberIdentifierUnknownIdentifier
const identifier = remoteParticipant.identifier;
  • state:返回的 string 表示远程参与者状态。 状态可以是下列值之一:
状态值 When 说明
Idle 初始状态 这是参与者的第一种状态
Connecting Idle 之后 参与者正在连接到通话时的过渡状态。
Ringing Connecting 之后 参与者收到 incomingCall 通知或 Teams 客户端响铃
Connected RingingConnectingEarlyMediaInLobby 之后 参与者接受了呼叫邀请或加入了呼叫。 媒体流向参与者。
Hold Connected 之后 通话中的参与者处于保持状态。
EarlyMedia Connecting 之后 在参与者连接到呼叫之前播放媒体
InLobby RingingConnecting、或 EarlyMedia 之后 参与者位于 Teams 会议大厅中。
Disconnected 最终状态 参与者已断开通话连接。 如果远程参与者断开了其网络连接,则两分钟后其状态将变为 Disconnected

一对一呼叫或组呼叫中远程参与者的状态:一对一或群组通话远程参与者的通话状态示意图。

Teams 会议中远程参与者的状态:Teams 会议远程参与者通话状态示意图。

const state = remoteParticipant.state;
  • callEndReason:返回一个对象,其中包含有关呼叫结束原因的其他信息。 属性 code 返回与原因关联的数字,subCode 返回与代码和原因关联的数字。 有关错误代码的详细信息,请参阅对通话结束响应代码进行故障排除
const callEndReason = remoteParticipant.callEndReason;
const callEndReasonCode = callEndReason.code
const callEndReasonSubCode = callEndReason.subCode
  • isMuted:返回的 Boolean 值表示本地静音状态。
const isMuted = remoteParticipant.isMuted;
  • isSpeaking:返回 Boolean 值,表示发送的非空音频的状态。
const isSpeaking = remoteParticipant.isSpeaking;
  • videoStreams:返回参与者发送的 RemoteVideoStream 对象集合。
const videoStreams = remoteParticipant.videoStreams; // [RemoteVideoStream, ...]
  • displayName:返回的 string 表示显示名称。 通信服务通话 SDK 不会为 Teams 用户设置此值。
const displayName = remoteParticipant.displayName;

调用

  • id:返回表示唯一调用标识符的字符串。
const callId = call.id;

info:返回有关呼叫的信息:

注意

此 API 以预览状态提供给开发者,可能根据我们收到的反馈更改。 请勿在生产环境中使用此 API。 若要使用此 API,请使用 Azure 通信服务呼叫 Web SDK 的 beta 版本

info:返回包含呼叫有关信息的对象。 属性 threadId 是表示 Teams 客户端中显示的聊天线程 ID 的字符串。

const callInfo = call.info;
const threadId = call.info.threadId;

remoteParticipants:返回的 remoteParticipant 对象集合表示 Teams 呼叫或 Teams 会议中其他参与者。

const remoteParticipants = call.remoteParticipants;

callerInfo:返回传入呼叫的 CallerInfo 对象。 属性 identifier 可以是下列的对象 CommunicationUserIdentifierMicrosoftTeamsUserIdentifierPhoneNumberIdentifierUnknownIdentifier 之一。 属性 displayName 是一个字符串,表示在设置时要显示的名称。

const callerIdentity = call.callerInfo.identifier;
const callerIdentity = call.callerInfo.displayName;

state:返回表示呼叫状态的字符串。 该属性可以具有下列值之一:

状态值 When 说明
None 初始状态 呼叫的初始状态。
Connecting None 之后 发出、加入或接受 Teams 呼叫或 Teams 会议时的状态。
Ringing Connecting 之后 远程参与者收到 incomingCall 事件或 Teams 客户端响铃。
EarlyMedia Ringing、或 Connecting 之后 在呼叫连接之前播放媒体。
Connected RingingEarlyMediaInLobbyLocalHoldRemoteHold 之后 呼叫已连接。 本地终结点与远程参与者之间有媒体流动。
LocalHold Connected 之后 通话被本地参与者暂停。 本地终结点与远程参与者之间没有媒体流动。
RemoteHold Connected 之后 通话被远程参与者暂停。 本地终结点与远程参与者之间没有媒体流动。
InLobby Ringing、或 Connecting 之后 远程参与者位于 Teams 会议大厅中。 本地终结点与远程参与者之间没有媒体流动。
Disconnecting 在任何状态之后 在通话进入 Disconnected 状态之前的过渡状态。
Disconnected 最终状态 呼叫的最终状态。 如果网络连接断开,则两分钟后状态将变为 Disconnected

一对一或组呼叫的状态:一对一或群组通话的通话状态示意图。

Teams 会议的状态:Teams 会议的通话状态示意图。

const callState = call.state;

callEndReason:返回对象 CallEndReason,其中包含有关呼叫结束的其他信息。 属性 code 返回与原因关联的数字,subCode 返回与代码和原因关联的数字。 有关错误代码的详细信息,请参阅对通话结束响应代码进行故障排除

const callEndReason = call.callEndReason;
const callEndReasonCode = callEndReason.code
const callEndReasonSubCode = callEndReason.subCode

direction:返回一个表示呼叫方向的 string。 该属性可以具有下列值之一:“Incoming”或 Outgoing

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

isMuted:返回的 Boolean 值表示本地静音状态。

const muted = call.isMuted;

isScreenSharingOn:如果向其他参与者发送屏幕共享流,则 Boolean 值返回为 true。

const isScreenSharingOn = call.isScreenSharingOn;

localVideoStreams:返回 LocalVideoStream 对象集合,表示要发送到远程参与者的视频流。

const localVideoStreams = call.localVideoStreams;

管理聊天线程

重要

可选的聊天 ID 仅在适用于 JavaScript 的通话 SDK 1.29.1 或更高版本中可用。 如果使用的是更早的版本,请务必以手动方式提供唯一的聊天 ID。

要拨打群组电话以及将参与者添加到现有通话,可以选择性地提供聊天 ID。 关联的聊天和通话具有单独的参与者列表。 在将参与者添加到通话之前,请将用户添加到聊天中,以提供最佳用户体验并满足信息屏障要求。 如果设置了信息屏障,在不将用户添加到聊天的情况下将用户添加到通话可能会导致异常。

考虑以下场景:Alice 给 Bob 拨打电话,然后 Alice 添加 Charlie,3 分钟后,Alice 将 Charlie 从通话中删除。

  1. 在 Alice、Bob 和 Charlie 之间创建一个聊天线程。 请保留聊天 threadId 供以后使用。
  2. Alice 在 TeamsCallAgent 实例上使用 startCall 方法呼叫 Bob 和 Charlie。
  3. 使用聊天图形 API 添加成员,将 Dan 添加到具有 threadId 的聊天线程
  4. Alice 使用 call 上的 addParticipant 方法将 Dan 添加到通话并指定 threadId
  5. Alice 使用 call 上的 removeParticipant 方法从通话中删除 Dan 并指定 threadId
  6. 使用聊天图形 API 删除成员,从具有 threadId 的聊天线程中删除 Dan

如果 Teams 用户停止通话记录,则记录将放入与线程关联的聊天中。 提供的聊天 ID 会影响 Teams 用户在 Teams 客户端中的体验。

对聊天 ID 的管理建议:

  • 通过添加另一个通话参与者来升级 1:1 电话呼叫:
    • 通过方法 addParticipant,可以提供可选参数聊天 ID。 如果未提供该参数,则会创建新的群组聊天,并将所有参与者添加到通话和聊天参与者列表中。 如果提供该参数,则 Teams 用户可以在 Teams 应用中查看与此群组聊天关联的正在进行的通话。 可以通过图形 API 创建新的群组聊天。
      addParticipant(participant: MicrosoftTeamsUserIdentifier | PhoneNumberIdentifier | MicrosoftTeamsAppIdentifier | UnknownIdentifier)
      
  • 与单个 Microsoft 365 用户和多个通话参与者发起群组通话:
    • 使用方法 startCall API,可以发起与多个参与者的群组通话,并且可以选择提供聊天 ID。 如果未提供该参数,则会创建新的群组聊天,并将所有 Microsoft 365 参与者添加到通话和聊天参与者列表中。 如果提供该参数,则 Teams 用户可以在 Teams 应用中查看与此群组聊天关联的正在进行的通话。 可以通过图形 API 创建新的群组聊天。
      startCall(MicrosoftTeamsUserIdentifier | PhoneNumberIdentifier | MicrosoftTeamsAppIdentifier | UnknownIdentifier)[])
      
    • 使用图形 API 获取身为参与者的 Teams 用户的现有聊天 ID,或者与参与者建立新的群组聊天:Teams 用户 ID 和“00000000-0000-0000-0000-000000000000”。
  • 与 2 名以上的 Microsoft 365 用户发起群组通话:
    • (可选方式)使用 ACS 通话 SDK 与 2 个以上的 Microsoft 365 用户进行群组通话时,SDK 默认会自动创建线程。
      startCall(MicrosoftTeamsUserIdentifier | PhoneNumberIdentifier | MicrosoftTeamsAppIdentifier | UnknownIdentifier)[])
      
    • 如果需要,开发人员可以提供唯一的聊天 ID 来发起群组通话或添加参与者。 在这种情况下,ACS 通话 SDK 将使用给定的聊天 ID 创建群组通话。 为 Teams 用户创建聊天线程,此线程将关联到 Teams 应用中用户的群组通话。 这样,他们便可以在通话期间聊天。 聊天线程管理可以通过图形 API 进行