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

从 Twilio Video 迁移到 Azure 通信服务

本文介绍如何将现有的 Twilio Video 实现迁移到 Azure 通信服务呼叫 SDK。 Twilio Video 和 Azure 通信服务呼叫 SDK 都是基于云的平台,可让开发人员将语音和视频通话功能添加到其 Web 应用程序。

但它们之间存在一些主要差异,可能会影响你对平台的选择,或者如果你决定迁移,则需要对现有代码进行一些更改。 在本文中,我们将比较这两个平台的主要特性和功能,并提供有关如何将现有 Twilio Video 实现迁移到 Azure 通信服务呼叫 SDK 的一些指导。

Azure 通信服务呼叫 SDK 中提供的主要功能

功能 Web (JavaScript) iOS Android 平台中立
安装 ✔️ ✔️ ✔️
导入 ✔️ ✔️ ✔️
身份验证 ✔️
Join ✔️ ✔️ ✔️
启动音频/扬声器 ✔️ ✔️ ✔️
静音 ✔️ ✔️ ✔️
取消静音 ✔️ ✔️ ✔️
启动视频 ✔️ ✔️ ✔️
停止视频 ✔️ ✔️ ✔️
虚拟背景 ✔️ ✔️ ✔️
呈现用户视频 ✔️ ✔️ ✔️
录制 ✔️
网络带宽管理 ✔️ ✔️ ✔️
服务质量 ✔️
数据中心选择 ✔️
预览 ✔️ ✔️ ✔️
安全性 ✔️
联网 ✔️
屏幕共享 ✔️
Rest API ✔️
Webhook ✔️
原始数据 ✔️ ✔️ ✔️
编解码器 ✔️
WebView ✔️ ✔️
视频设备 ✔️ ✔️ ✔️
扬声器设备 ✔️ ✔️ ✔️
麦克风设备 ✔️ ✔️ ✔️
数据通道 API ✔️ ✔️ ✔️
分析/视频见解 ✔️
诊断工具 ✔️
报表 ✔️
CallKit(仅限 iOS) ✔️
画中画 ✔️ ✔️

先决条件

  1. Azure 帐户: 确保 Azure 帐户处于活动状态。 新用户可以在 Microsoft Azure 创建免费帐户。
  2. Node.js 18:确保系统上安装了 Node.js 18。 从 Node.js 下载。
  3. 通信服务资源:通过 Azure 门户设置通信服务资源并记下连接字符串。
  4. Azure CLI:按照说明在 Windows.上安装 Azure CLI
  5. 用户访问令牌:生成用户访问令牌以实例化呼叫客户端。 可以使用 Azure CLI 创建一个,如下所示:
az communication identity token issue --scope voip --connection-string "yourConnectionString"

有关详细信息,请参阅使用 Azure CLI 创建和管理访问令牌

对于 Teams 用户的视频通话:

UI 库

Azure 通信服务 UI 库简化了使用 Azure 通信服务创建现代通信用户界面的过程。 它提供一系列现成的 UI 组件,可轻松集成到应用程序中。

此开源预建控件集可让你使用 Fluent UI SDK 组件创建美观的设计,并开发高质量的音频/视频通信体验。 有关详细信息,请查看 Azure 通信服务 UI 库概述。 概述包括有关 Web 和移动平台的全面信息。

安装

安装 Azure 通信服务呼叫 SDK

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

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

从项目中删除 Twilio SDK

可以通过卸载包从项目中删除 Twilio SDK。

npm uninstall twilio-video

对象模型

以下类和接口用于处理 Azure 通信服务通话 SDK 的某些主要功能:

Name 描述
CallClient 通话 SDK 的主入口点。
AzureCommunicationTokenCredential 实现用于实例化 CallAgent 的 CommunicationTokenCredential 接口。
CallAgent 启动和管理呼叫。
设备管理器 管理媒体设备。
调用 表示呼叫。
LocalVideoStream 为本地系统上的相机设备创建本地视频流。
RemoteParticipant 表示通话中的远程参与者。
RemoteVideoStream 表示来自远程参与者的远程视频流。
LocalAudioStream 表示本地麦克风设备的本地音频流。
AudioOptions 发出传出呼叫或加入组呼叫时提供给参与者的音频选项。
AudioIssue 表示呼叫调查音频问题的结束。 示例响应可能是 NoLocalAudio -其他参与者无法听到我,或 LowVolume - 呼叫音频音量太低。

在 Teams 通话中使用 ACS 呼叫时,存在一些差异:

  • 不使用 CallAgent - 而使用 TeamsCallAgent 启动和管理 Teams 呼叫。
  • 不使用 Call - 而使用 TeamsCall 来表示 Teams 通话。

初始化呼叫 SDK (CallClient/CallAgent)

使用 CallClient初始化 CallAgent 实例。 createCallAgent 方法使用 CommunicationTokenCredential 作为参数。 它接受用户访问令牌

设备管理器

Twilio

Twilio 没有设备管理器模拟。 轨道是使用系统的默认设备创建的。 若要自定义设备,请通过以下方式获取所需的源轨道:

navigator.mediaDevices.getUserMedia()

并将其传递给轨道创建方法。

Azure 通信服务

const { CallClient } = require('@azure/communication-calling');  
const { AzureCommunicationTokenCredential} = require('@azure/communication-common'); 

const userToken = '<USER_TOKEN>';  
const tokenCredential = new AzureCommunicationTokenCredential(userToken); 

callClient = new CallClient(); 
const callAgent = await callClient.createCallAgent(tokenCredential, {displayName: 'optional user name'});

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

const deviceManager = await callClient.getDeviceManager();

// Get a list of available video devices for use.  
const localCameras = await deviceManager.getCameras(); 

// Get a list of available microphone devices for use.  
const localMicrophones = await deviceManager.getMicrophones();  

// Get a list of available speaker devices for use.  
const localSpeakers = await deviceManager.getSpeakers();

获取设备权限

Twilio

Twilio Video 要求对轨道创建具有设备权限。

Azure 通信服务

提示用户授予相机和/或麦克风权限:

const result = await deviceManager.askDevicePermission({audio: true, video: true});

输出将返回,其中包含指示是否授予音频和视频权限的对象:

console.log(result.audio);  console.log(result.video);

启动通话

Twilio

import * as TwilioVideo from 'twilio-video';

const twilioVideo = TwilioVideo; 
let twilioRoom; 

twilioRoom = await twilioVideo.connect('token', { name: 'roomName', audio: false, video: false });

Azure 通信服务

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

创建和发起呼叫的操作是同步的。 使用 call 实例可以订阅通话事件。 订阅 stateChanged 事件以获取值更改。

call.on('stateChanged', async () =\> {  console.log(\`Call state changed: \${call.state}\`) });

1:1 呼叫

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

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

聊天室通话

若要加入 Room 通话,可以将具有 roomId 属性的上下文对象实例化为聊天室标识符。 若要加入通话,请使用 join 方法并传递上下文实例。

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

通过聊天室,应用程序开发人员可以更好地控制谁可以加入通话、何时见面以及如何协作。 若要了解有关聊天室的详细信息,请参阅聊天室概述,或参阅快速入门:加入聊天室通话

群组呼叫

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

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

Teams 呼叫

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

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

接受和加入通话

Twilio

使用 Twilio 视频 SDK 时,参与者在加入聊天室后创建;它没有任何关于其他聊天室的信息。

Azure 通信服务

Azure 通信服务具有 CallAgent 实例,该实例在登录标识收到传入呼叫时发出 incomingCall 事件。

callAgent.on('incomingCall', async (call) =\>{
    // Incoming call
    });

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

视频开启时启动、加入或接受呼叫,如果指定的视频相机设备正由另一个进程使用,或者系统中禁用了相机,则呼叫将在视频关闭时开始,并返回 cameraStartFailed: true 呼叫诊断。

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

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

  // Get information about this Call.  
  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);

启动呼叫、加入呼叫或接受呼叫后,还可以使用 callAgentcallsUpdated 事件接收新 Call 对象的通知并开始订阅该事件。

callAgent.on('callsUpdated', (event) => { 
  event.added.forEach((call) => { 
    // User joined call 
  }); 
  
  event.removed.forEach((call) => { 
    // User left call 
  }); 
});

有关 Azure 通信服务 Teams 实现,请参阅如何接收 Teams 传入呼叫

添加和删除通话参与者

Twilio

参与者无法从 Twilio 聊天室添加或删除,他们需要加入聊天室或与聊天室本身断开连接。

可以通过以下方式访问 Twilio 聊天室中的本地参与者:

let localParticipant = twilioRoom.localParticipant;

Twilio 聊天室中的远程参与者通过以唯一参与者 SID 作为键的地图来表示:

twilioRoom.participants;

Azure 通信服务

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

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

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

添加参与者:

若要将参与者添加到通话中,可以使用 addParticipant。 提供一种标识符类型。 它将同步返回 remoteParticipant 实例。

当参与者成功添加到通话中时,通话中将引发 remoteParticipantsUpdated 事件。

const userIdentifier = { communicationUserId: '<Azure_Communication_Services_USER_ID>' }; 
const remoteParticipant = call.addParticipant(userIdentifier);

删除参与者:

若要从通话中删除参与者,请使用 removeParticipant。 需要传递其中一种标识符类型。 从通话中删除参与者后,此方法异步进行解析。 还将从 remoteParticipants 集合中删除该参与者。

const userIdentifier = { communicationUserId: '<Azure_Communication_Services_USER_ID>' }; 
await call.removeParticipant(userIdentifier);

订阅通话的 remoteParticipantsUpdated 事件,以在将新参与者添加到通话或从通话中删除时收到通知。

call.on('remoteParticipantsUpdated', e => {
    e.added.forEach(remoteParticipant => {
        // Subscribe to new remote participants that are added to the call
    });
 
    e.removed.forEach(remoteParticipant => {
        // Unsubscribe from participants that are removed from the call
    })

});

订阅远程参与者的 stateChanged 事件以获取值更改。

remoteParticipant.on('stateChanged', () => {
    console.log(`Remote participants state changed: ${remoteParticipant.state}`)
});

视频通话

启动和停止视频

Twilio

const videoTrack = await twilioVideo.createLocalVideoTrack({ constraints }); 
const videoTrackPublication = await localParticipant.publishTrack(videoTrack, { options });

默认情况下,相机处于启用状态。 如有必要,可以禁用和启用它:

videoTrack.disable();

或:

videoTrack.enable();

如果有稍后创建的视频轨道,请在本地附加它:

const videoElement = videoTrack.attach();
const localVideoContainer = document.getElementById( localVideoContainerId );
localVideoContainer.appendChild(videoElement);

Twilio 轨道依赖于默认输入设备,并反映默认值的更改。 若要更改输入设备,需要取消发布上一个视频轨道:

localParticipant.unpublishTrack(videoTrack);

然后,使用正确的约束创建新的视频轨道。

Azure 通信服务

若要在通话时开始视频,需要在 deviceManager 对象上使用 getCameras 方法来枚举相机。 接下来使用所需相机创建一个新的 LocalVideoStream 实例,然后将 LocalVideoStream 对象传递给现有通话对象的 startVideo 方法:

const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);

成功开始发送视频后,系统会将一个视频类型的 LocalVideoStream 实例添加到通话实例上的 localVideoStreams 集合。

const localVideoStream = call.localVideoStreams.find( (stream) =\> { return stream.mediaStreamType === 'Video'} );

若要在通话时停止本地视频,请传递用于视频的 localVideoStream 实例:

await call.stopVideo(localVideoStream);

localVideoStream 实例呼叫 switchSource 来发送视频时,可切换到不同的相机设备:

const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);

如果指定的视频设备被另一个进程占用或在系统中被禁用:

  • 在通话中,如果视频已关闭并且使用 call.startVideo() 启动视频,则此方法将返回 SourceUnavailableError 并且 cameraStartFailed 设置为 true。
  • 调用 localVideoStream.switchSource() 方法将导致 cameraStartFailed 设置为 true。 有关如何诊断呼叫相关问题的详细信息,请参阅呼叫诊断指南

要验证本地视频是开启还是关闭,可以使用返回 true 或 false 的 isLocalVideoStarted API:

call.isLocalVideoStarted;

要侦听本地视频的更改,可以订阅和取消订阅 isLocalVideoStartedChanged 事件:

// Subscribe to local video event
call.on('isLocalVideoStartedChanged', () => {
    // Callback();
});
// Unsubscribe from local video event
call.off('isLocalVideoStartedChanged', () => {
    // Callback();
});

呈现远程用户的视频

Twilio

只要远程参与者发布视频轨道,便需要附加。 聊天室或远程参与者的 trackSubscribed 事件可用于检测何时可以附加轨道:

twilioRoom.on('participantConneted', (participant) => {
 participant.on('trackSubscribed', (track) => {
   const remoteVideoElement = track.attach();
   const remoteVideoContainer = document.getElementById(remoteVideoContainerId + participant.identity);
   remoteVideoContainer.appendChild(remoteVideoElement);
 });
});

twilioRoom..on('trackSubscribed', (track, publication, participant) => {
   const remoteVideoElement = track.attach();
   const remoteVideoContainer = document.getElementById(remoteVideoContainerId + participant.identity);
   remoteVideoContainer.appendChild(remoteVideoElement);
 });
});

Azure 通信服务

若要列出远程参与者的视频流和屏幕共享流,请检查 videoStreams 集合:

const remoteVideoStream: RemoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType: MediaStreamType = remoteVideoStream.mediaStreamType;

要呈现 RemoteVideoStream,必须订阅其 isAvailableChanged 事件。 如果 isAvailable 属性更改为 true,则表示远程参与者正在发送流。 发生该情况后,请创建一个新的 VideoStreamRenderer 实例,然后使用异步 createView 方法创建一个新的 VideoStreamRendererView 实例。 然后,可以将 view.target 附加到任何 UI 元素。

只要远程流的可用性发生更改,就可以销毁整个 VideoStreamRenderer 或特定 VideoStreamRendererView。 如果决定保留它们,则会显示空白视频帧。

// Reference to the html's div where we would display a grid of all remote video streams from all participants.
let remoteVideosGallery = document.getElementById('remoteVideosGallery');

subscribeToRemoteVideoStream = async (remoteVideoStream) => {
   let renderer = new VideoStreamRenderer(remoteVideoStream);
    let view;
    let remoteVideoContainer = document.createElement('div');
    remoteVideoContainer.className = 'remote-video-container';

    let loadingSpinner = document.createElement('div');
    // See the css example below for styling the loading spinner.
    loadingSpinner.className = 'loading-spinner';
    remoteVideoStream.on('isReceivingChanged', () => {
        try {
            if (remoteVideoStream.isAvailable) {
                const isReceiving = remoteVideoStream.isReceiving;
                const isLoadingSpinnerActive = remoteVideoContainer.contains(loadingSpinner);
                if (!isReceiving && !isLoadingSpinnerActive) {
                    remoteVideoContainer.appendChild(loadingSpinner);
                } else if (isReceiving && isLoadingSpinnerActive) {
                    remoteVideoContainer.removeChild(loadingSpinner);
                }
            }
        } catch (e) {
            console.error(e);
        }
    });

    const createView = async () => {
        // Create a renderer view for the remote video stream.
        view = await renderer.createView();
        // Attach the renderer view to the UI.
        remoteVideoContainer.appendChild(view.target);
        remoteVideosGallery.appendChild(remoteVideoContainer);
    }

    // Remote participant has switched video on/off
    remoteVideoStream.on('isAvailableChanged', async () => {
        try {
            if (remoteVideoStream.isAvailable) {
                await createView();
            } else {
                view.dispose();
                remoteVideosGallery.removeChild(remoteVideoContainer);
            }
        } catch (e) {
            console.error(e);
        }
    });

    // Remote participant has video on initially.
    if (remoteVideoStream.isAvailable) {
        try {
            await createView();
        } catch (e) {
            console.error(e);
        }
    }
    
    console.log(`Initial stream size: height: ${remoteVideoStream.size.height}, width: ${remoteVideoStream.size.width}`);
    remoteVideoStream.on('sizeChanged', () => {
        console.log(`Remote video stream size changed: new height: ${remoteVideoStream.size.height}, new width: ${remoteVideoStream.size.width}`);
    });
}

订阅远程参与者的 videoStreamsUpdated 事件,以在远程参与者新增视频流并删除视频流时收到通知。

remoteParticipant.on('videoStreamsUpdated', e => {
    e.added.forEach(remoteVideoStream => {
        // Subscribe to new remote participant's video streams
    });

    e.removed.forEach(remoteVideoStream => {
        // Unsubscribe from remote participant's video streams
    });
});

虚拟背景

Twilio

要使用虚拟背景,请安装 Twilio 帮助程序库:

npm install @twilio/video-processors

创建并加载新的 Processor 实例:

import { GaussianBlurBackgroundProcessor } from '@twilio/video-processors';

const blurProcessor = new GaussianBlurBackgroundProcessor({ assetsPath: virtualBackgroundAssets });

await blurProcessor.loadModel();

只要加载了模型,便可使用 addProcessor 方法将背景添加到视频轨道:

videoTrack.addProcessor(processor, {  inputFrameBufferType: 'video',  outputFrameBufferContextType: 'webgl2' });

Azure 通信服务

使用 npm install 命令安装适用于 JavaScript 的 Azure 通信服务效果 SDK

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

注意

要将视频效果与 Azure 通信呼叫 SDK 配合使用,在创建 LocalVideoStream 后,需要获取 LocalVideoStream 的 VideoEffects 功能 API 来启动/停止视频效果:

import * as AzureCommunicationCallingSDK from '@azure/communication-calling'; 

import { BackgroundBlurEffect, BackgroundReplacementEffect } from '@azure/communication-calling-effects'; 

// Get the video effects feature API on the LocalVideoStream 
// (here, localVideoStream is the LocalVideoStream object you created while setting up video calling)
const videoEffectsFeatureApi = localVideoStream.feature(AzureCommunicationCallingSDK.Features.VideoEffects); 

// Subscribe to useful events 
videoEffectsFeatureApi.on(‘effectsStarted’, () => { 
    // Effects started
});

videoEffectsFeatureApi.on(‘effectsStopped’, () => { 
    // Effects stopped
}); 

videoEffectsFeatureApi.on(‘effectsError’, (error) => { 
    // Effects error
});

模糊背景:

// Create the effect instance 
const backgroundBlurEffect = new BackgroundBlurEffect(); 

// Recommended: Check support 
const backgroundBlurSupported = await backgroundBlurEffect.isSupported(); 

if (backgroundBlurSupported) { 
    // Use the video effects feature API we created to start effects
    await videoEffectsFeatureApi.startEffects(backgroundBlurEffect); 
}

要对图像使用自定义背景替换,需要提供所需的 URL 作为此效果背景的图像。 支持的图像格式包括:PNG、JPG、JPEG、TIFF 和 BMP。 支持的纵横比为 16:9。

const backgroundImage = 'https://linkToImageFile'; 

// Create the effect instance 
const backgroundReplacementEffect = new BackgroundReplacementEffect({ 
    backgroundImageUrl: backgroundImage
}); 

// Recommended: Check support
const backgroundReplacementSupported = await backgroundReplacementEffect.isSupported(); 

if (backgroundReplacementSupported) { 
    // Use the video effects feature API as before to start/stop effects 
    await videoEffectsFeatureApi.startEffects(backgroundReplacementEffect); 
}

通过以下配置方法传递图像来更改此效果的图像:

const newBackgroundImage = 'https://linkToNewImageFile'; 

await backgroundReplacementEffect.configure({ 
    backgroundImageUrl: newBackgroundImage
});

要切换效果,在视频效果功能 API 上使用相同的方法:

// Switch to background blur 
await videoEffectsFeatureApi.startEffects(backgroundBlurEffect); 

// Switch to background replacement 
await videoEffectsFeatureApi.startEffects(backgroundReplacementEffect);

如果想要检查哪些效果处于活动状态,可以随时使用 activeEffects 属性。 属性 activeEffects 返回具有当前活动效果名称的数组,如果没有活动效果,则返回空数组。

// Using the video effects feature api
const currentActiveEffects = videoEffectsFeatureApi.activeEffects;

停止效果:

await videoEffectsFeatureApi.stopEffects();

音频

启动和停止音频

Twilio

const audioTrack = await twilioVideo.createLocalAudioTrack({ constraints });
const audioTrackPublication = await localParticipant.publishTrack(audioTrack, { options });

默认情况下,麦克风处于启用状态。 可以根据需要禁用和启用它:

audioTrack.disable();

audioTrack.enable();

本地参与者应以与视频轨道相同的方式附加任何已创建的音频轨道:

const audioElement = audioTrack.attach();
const localAudioContainer = document.getElementById(localAudioContainerId);
localAudioContainer.appendChild(audioElement);

由远程参与者:

twilioRoom.on('participantConneted', (participant) => {
 participant.on('trackSubscribed', (track) => {
   const remoteAudioElement = track.attach();
   const remoteAudioContainer = document.getElementById(remoteAudioContainerId + participant.identity);
   remoteAudioContainer.appendChild(remoteAudioElement);
 });
});

或:

twilioRoom..on('trackSubscribed', (track, publication, participant) => {
   const remoteAudioElement = track.attach();
   const remoteAudioContainer = document.getElementById(remoteAudioContainerId + participant.identity);
   remoteVideoContainer.appendChild(remoteAudioElement);
 });
});

无法在 Twilio 视频 SDK 中将传入的音频静音。

Azure 通信服务

await call.startAudio();

若要将本地终结点设置为静音或取消静音,可以使用静音和取消静音异步 API:

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

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

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

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

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

检测主导发言人

Twilio

若要检测会议室中最响亮的参与者,请使用主导发言人 API。 加入至少有 2 个参与者的组聊天室时,可以在连接选项中启用它:

twilioRoom = await twilioVideo.connect('token', { 
name: 'roomName', 
audio: false, 
video: false,
dominantSpeaker: true
}); 

当聊天室中最响亮的发言人发生改变时,发出 dominantSpeakerChanged 事件:

twilioRoom.on('dominantSpeakerChanged', (participant) => {
    // Highlighting the loudest speaker
});

Azure 通信服务

通话的主导发言人是核心通话 API 的扩展功能。 它可让你在通话中获取活跃的发言人的列表。 主导发言人列表是排名列表,列表中的第一个元素表示通话中最后一个活跃的发言人,依此类推。

为获取通话中的主导说话人,首先需获取通话主导说话人功能 API 对象:

const callDominantSpeakersApi = call.feature(Features.CallDominantSpeakers);

接下来,通过调用 dominantSpeakers 获取主导发言人列表。 其类型为 DominantSpeakersInfo,具有以下成员:

  • speakersList 包含通话中已排名的主导说话人列表。 这些由其参与者 ID 表示。
  • timestamp 是通话中的主导发言人的最新更新时间。
let dominantSpeakers: DominantSpeakersInfo = callDominantSpeakersApi.dominantSpeakers;

还可以订阅 dominantSpeakersChanged 事件,了解主导发言人列表何时发生更改。

const dominantSpeakersChangedHandler = () => {
    // Get the most up-to-date list of dominant speakers
    let dominantSpeakers = callDominantSpeakersApi.dominantSpeakers;
};
callDominantSpeakersApi.on('dominantSpeakersChanged', dominantSpeakersChangedHandler);

启用屏幕共享

Twilio

若要在 Twilio 视频中共享屏幕,请通过 navigator.mediaDevices 获取源轨道:

基于 Chromium 的浏览器:

const stream = await navigator.mediaDevices.getDisplayMedia({
   audio: false,
   video: true
 });
const track = stream.getTracks()[0];

Firefox 和 Safari:

const stream = await navigator.mediaDevices.getUserMedia({ mediaSource: 'screen' });
const track = stream.getTracks()[0];

获取屏幕共享轨道,然后你可以采用与休闲视频轨道相同的方式发布和管理它(请参阅“视频”部分)。

Azure 通信服务

若要在通话时启动屏幕共享,可以使用异步 API startScreenSharing

await call.startScreenSharing();

成功开始发送屏幕共享后,ScreenSharing 类型的 LocalVideoStream 实例将会创建并添加到通话实例上的 localVideoStreams 集合。

const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing'} );

若要在通话时停止屏幕共享,可以使用异步 API stopScreenSharing

await call.stopScreenSharing();

若要验证屏幕共享是开启还是关闭,可以使用返回 true 或 false 的 isScreenSharingOn API:

call.isScreenSharingOn;

若要侦听屏幕共享的更改,可订阅和取消订阅 isScreenSharingOnChanged 事件:

// Subscribe to screen share event
call.on('isScreenSharingOnChanged', () => {
    // Callback();
});
// Unsubscribe from screen share event
call.off('isScreenSharingOnChanged', () => {
    // Callback();
});

媒体质量统计信息

Twilio

若要收集实时媒体统计信息,请使用“getStats”方法。

const stats = twilioRoom.getStats();

Azure 通信服务

媒体质量统计信息是核心通话 API 的扩展功能。 首先需要获取 mediaStatsFeature API 对象:

const mediaStatsFeature = call.feature(Features.MediaStats);

若要接收媒体统计信息数据,可以订阅 sampleReported 事件或 summmaryReported 事件:

  • sampleReported 事件每秒触发一次。 适合用作 UI 显示或你自己的数据管道的数据源。
  • summmaryReported 事件包含间隔内数据的聚合值。 当你只需要摘要时非常有用。

如果要控制 summmaryReported 事件的间隔,则需要定义 MediaStatsCollectorOptions 类型的 mediaStatsCollectorOptions。 否则,SDK 使用默认值。

const mediaStatsCollectorOptions: SDK.MediaStatsCollectorOptions = {
    aggregationInterval: 10,
    dataPointsPerAggregation: 6
};

const mediaStatsCollector = mediaStatsFeature.createCollector(mediaStatsSubscriptionOptions);

mediaStatsCollector.on('sampleReported', (sample) => {
    console.log('media stats sample', sample);
});

mediaStatsCollector.on('summaryReported', (summary) => {
    console.log('media stats summary', summary);
});

如果不需要使用媒体统计信息收集器,则可以调用 mediaStatsCollector 的处理方法。

mediaStatsCollector.dispose();

无需在每次调用结束时调用 mediaStatsCollector 的处理方法。 调用结束时,收集器将在内部回收。

有关详细信息,请参阅 媒体质量统计信息

诊断

Twilio

为了测试连接性,Twilio 提供预检 API。 这是一个测试调用,用于识别信令和媒体连接问题。

启动测试需要访问令牌:

const preflightTest = twilioVideo.runPreflight(token);

// Emits when particular call step completes
preflightTest.on('progress', (progress) => {
  console.log(`Preflight progress: ${progress}`);
});

// Emits if the test has failed and returns error and partial test results
preflightTest.on('failed', (error, report) => {
  console.error(`Preflight error: ${error}`);
  console.log(`Partial preflight test report: ${report}`);
});

// Emits when the test has been completed successfully and returns the report
preflightTest.on('completed', (report) => {
  console.log(`Preflight test report: ${report}`);
});

在调用期间识别网络问题的另一种方法是使用网络质量 API 来监视参与者的网络并提供质量指标。 当参与者加入组聊天室时,可以在连接选项中启用它:

twilioRoom = await twilioVideo.connect('token', { 
    name: 'roomName', 
    audio: false, 
    video: false,
    networkQuality: {
        local: 3, // Local Participant's Network Quality verbosity
        remote: 1 // Remote Participants' Network Quality verbosity
    }
});

当参与者的网络质量发生更改时,它会生成 networkQualityLevelChanged 事件:

participant.on(networkQualityLevelChanged, (networkQualityLevel, networkQualityStats)  => {
    // Processing Network Quality stats
});

Azure 通信服务

Azure 通信服务提供一项称为 "User Facing Diagnostics" (UFD) 的功能,可用于检查调用的各种属性来识别问题。 面向用户的诊断事件可能是由一些基础问题(网络不佳、用户麦克风静音)引起的,这可能导致用户通话体验不佳。

面向用户的诊断是核心调用 API 的扩展功能,可用于诊断活动呼叫。

const userFacingDiagnostics = call.feature(Features.UserFacingDiagnostics);

订阅“diagnosticChanged”事件,以监视何时发生任何面向用户的诊断更改:

/**
 *  Each diagnostic has the following data:
 * - diagnostic is the type of diagnostic, e.g. NetworkSendQuality, DeviceSpeakWhileMuted
 * - value is DiagnosticQuality or DiagnosticFlag:
 *     - DiagnosticQuality = enum { Good = 1, Poor = 2, Bad = 3 }.
 *     - DiagnosticFlag = true | false.
 * - valueType = 'DiagnosticQuality' | 'DiagnosticFlag'
 */
const diagnosticChangedListener = (diagnosticInfo: NetworkDiagnosticChangedEventArgs | MediaDiagnosticChangedEventArgs) => {
    console.log(`Diagnostic changed: ` +
        `Diagnostic: ${diagnosticInfo.diagnostic}` +
        `Value: ${diagnosticInfo.value}` +
        `Value type: ${diagnosticInfo.valueType}`);

    if (diagnosticInfo.valueType === 'DiagnosticQuality') {
        if (diagnosticInfo.value === DiagnosticQuality.Bad) {
            console.error(`${diagnosticInfo.diagnostic} is bad quality`);

        } else if (diagnosticInfo.value === DiagnosticQuality.Poor) {
            console.error(`${diagnosticInfo.diagnostic} is poor quality`);
        }

    } else if (diagnosticInfo.valueType === 'DiagnosticFlag') {
        if (diagnosticInfo.value === true) {
            console.error(`${diagnosticInfo.diagnostic}`);
        }
    }
};

userFacingDiagnostics.network.on('diagnosticChanged', diagnosticChangedListener);
userFacingDiagnostics.media.on('diagnosticChanged', diagnosticChangedListener);

若要详细了解面向用户的诊断和可用的不同诊断值,请参阅面向用户的诊断

Azure 通信服务还提供调用前诊断 API。 若要访问调用前 API,需要初始化 callClient 并预配一个 Azure 通信服务访问令牌。 然后,可以访问 PreCallDiagnostics 功能和 startTest 方法。

import { CallClient, Features} from "@azure/communication-calling";
import { AzureCommunicationTokenCredential } from '@azure/communication-common';

const callClient = new CallClient(); 
const tokenCredential = new AzureCommunicationTokenCredential("INSERT ACCESS TOKEN");
const preCallDiagnosticsResult = await callClient.feature(Features.PreCallDiagnostics).startTest(tokenCredential);

呼叫前 API 返回设备的完整诊断信息,包括设备权限、可用性和兼容性、呼叫质量统计信息和呼叫中诊断等详细信息。 结果作为 PreCallDiagnosticsResult 对象返回。

export declare type PreCallDiagnosticsResult  = {
    deviceAccess: Promise<DeviceAccess>;
    deviceEnumeration: Promise<DeviceEnumeration>;
    inCallDiagnostics: Promise<InCallDiagnostics>;
    browserSupport?: Promise<DeviceCompatibility>;
    id: string;
    callMediaStatistics?: Promise<MediaStatsCallFeature>;
};

可以在调用前诊断中详细了解如何确保调用前就绪。

事件侦听器

Twilio

twilioRoom.on('participantConneted', (participant) => { 
// Participant connected 
}); 

twilioRoom.on('participantDisconneted', (participant) => { 
// Participant Disconnected 
});

Azure 通信服务

JavaScript 呼叫 SDK 中的每个对象都有属性和集合。 其值在对象的整个生存期内都会更改。 使用 on() 方法订阅对象的事件,使用 off() 方法取消订阅对象的事件。

属性

  • 必须检查其初始值,并订阅 '\<property\>Changed' 事件,以便未来对值进行更新。

集合

  • 必须检查其初始值,并订阅 '\<collection\>Updated' 事件,以便未来对值进行更新。
  • '\<collection\>Updated' 事件的负载具有 added 数组,其中包含添加到集合的值。
  • '\<collection\>Updated' 事件的负载还具有已移除的数组,其中包含从集合中移除的值。

离开和结束会话

Twilio

twilioVideo.disconnect();

Azure 通信服务

call.hangUp();

// Set the 'forEveryone' property to true to end call for all participants
call.hangUp({ forEveryone: true });

清理

如果想要清理并删除通信服务订阅,可以删除资源或资源组。

先决条件

  1. Azure 帐户: 确保 Azure 帐户处于活动状态。 新用户可以在 Microsoft Azure 创建免费帐户。
  2. 通信服务资源:通过 Azure 门户设置通信服务资源并记下连接字符串。
  3. Azure CLI:按照说明在 Windows.上安装 Azure CLI
  4. 用户访问令牌:生成用户访问令牌以实例化呼叫客户端。 可以使用 Azure CLI 创建一个,如下所示:
az communication identity token issue --scope voip --connection-string "yourConnectionString"

有关详细信息,请参阅使用 Azure CLI 创建和管理访问令牌

对于 Teams 用户的视频通话:

UI 库

Azure 通信服务 UI 库简化了使用 Azure 通信服务呼叫创建现代通信用户界面的过程。 它提供一系列现成的 UI 组件,可轻松集成到应用程序中。

此开源预建控件集可让你使用 Fluent UI SDK 组件创建美观的设计,并开发高质量的音频/视频通信体验。 有关详细信息,请查看 Azure 通信服务 UI 库概述。 概述包括有关 Web 和移动平台的全面信息。

安装

若要开始从 Twilio Video 迁移,第一步是将适用于 iOS 的 Azure 通信服务呼叫 SDK 安装到项目中。 可以使用Cocoapods配置这些参数。

  1. 若要为应用程序创建 Podfile,请打开终端并导航到项目文件夹,然后运行:

pod init

  1. 将以下代码添加到 Podfile 并保存(确保“目标对象”与项目名称匹配):
platform :ios, '13.0' 
use_frameworks! 
  
target 'AzureCommunicationCallingSample' do 
  pod 'AzureCommunicationCalling', '~> 2.6.0' 
end 
  1. 设置 .xcworkspace 项目
pod install
  1. 使用 Xcode 打开 pod install 创建的 .xcworkspace

向 SDK 进行身份验证

为了能够使用 Azure 通信服务呼叫 SDK,需要使用访问令牌进行身份验证。

Twilio

以下代码片段假定 Twilio 服务的有效访问令牌的可用性。

在 Twilio Video 中,访问令牌用于连接到聊天室。 通过将令牌传递给 ConnectOptions,可以创建用于创建或连接聊天室的选项。

let connectOptions = ConnectOptions(token: accessToken) { 
 // Twilio Connect options goes here 
} 
 
let room =   TwilioVideoSDK.connect(
    options: connectOptions, 
    delegate: // The Room Delegate
)

Azure 通信服务

以下代码片段需要有效的访问令牌来启动 CallClient

需要有效的令牌。 有关详细信息,请参阅创建和管理访问令牌

// Create an instance of CallClient 
let callClient = CallClient() 
 
// A reference to the call agent, it will be initialized later 
var callAgent: CallAgent? 
 
// Embed the token in a CommunicationTokenCredential object 
let userCredential = try? CommunicationTokenCredential(token: "<USER_TOKEN>") 
 
// Create a CallAgent that will be used later to initiate or receive calls 
callClient.createCallAgent(userCredential: userCredential) { callAgent, error in 
 if error != nil { 
        // Raise the error to the user and return 
 } 
 self.callAgent = callAgent         
} 

类引用

类名 说明
CallClient 表示调用 SDK 入口点的主类。
CommunicationTokenCredential Azure 通信服务用户令牌凭据
CallAgent 负责代表经过身份验证的用户管理调用的类

发起传出呼叫

Twilio

Twilio Video 有 Room 的概念,如果用户 Bob 想要与客户端 Alice Bob 进行呼叫,可以创建聊天室,Alice 必须通过实现推送通知等功能来连接到聊天室。

连接到聊天室

当用户 Bob 或用户 Alice 想要创建或连接到聊天室时,并且他们具有有效的访问令牌时。 他们可以传递要创建或作为 ConnectOptions 参数进行连接的聊天室名称。

let connectOptions = ConnectOptions(token: accessToken) { builder in 
 builder.roomName = "the-room"  
} 
 
room = TwilioVideoSDK.connect(options: connectOptions, delegate: self)

Azure 通信服务

连接到呼叫

CommunicationUserIdentifier 表示使用标识 SDK 或 REST API 创建的用户标识。 如果应用程序不使用 Microsoft Teams 互操作性或电话功能,则它是唯一使用的标识符。

使用 Azure 通信服务呼叫 SDK 发起呼叫包括以下步骤:

  1. 创建 StartCallOptions 对象
  2. 创建 CommunicationUserIdentifier 数组
  3. 对之前创建的 CallAgent 调用 startCall 方法
let startCallOptions = StartCallOptions() // 1 
let callees = [CommunicationUserIdentifier(“<USER_ID>”)] // 2 
 
callAgent?.startCall(participants: callees, options: startCallOptions) { call, error in 
    // Check for error if no Error and the call object isn't nil then the call is being established       
}

连接到 Teams 的通话

使用外部标识

连接到团队通话与连接到单人通话几乎相同。 客户端应用程序需要将 JoinCallOptionsTeamsMeeting locator 一起使用,而不是使用 StartCallOptions

可以使用图形 API 来检索 Teams 会议链接。 可以在 Graph 文档中详细了解 Graph API。

let joinCallOptions = JoinCallOptions()
let teamsMeetingLinkLocator = TeamsMeetingLinkLocator(meetingLink: meetingLink)
    callAgent?.join(with: teamsMeetingLinkLocator, joinCallOptions: joinCallOptions) { call, error in
    // Handle error or set a CallDelegate delegate on the call if no error
}

接受和加入通话

Twilio

Twilio Video 使用 Room 的概念。 不同的客户端可以通过加入同一聊天室来建立通信。 因此,接受和加入通话不是直接的替代方法。

Azure 通信服务

接收传入呼叫

若要接受呼叫,必须先将应用程序配置为接收传入呼叫。

注册推送通知并处理传入的推送通知

通话客户端可以选择接收推送通知以接收传入呼叫。 本指南介绍如何为 Azure 通信服务呼叫设置 APNS。

设置 CallAgentDelegate

Azure 通信服务呼叫 SDK 有 CallAgentDelegate,后者具有在传入呼叫期间调用的方法。

class ApplicationCallAgentDelegate: NSObject, CallAgentDelegate { 
 
    func callAgent(_ callAgent: CallAgent, didUpdateCalls args: CallsUpdatedEventArgs) {} 
 
    func callAgent(_ callAgent: CallAgent, didRecieveIncomingCall incomingCall: IncomingCall) { 
        // This is called when the application receives an incoming call 
        // An application could use this callback to  display an incoming call banner 
        // or report an incoming call to CallKit 
    } 
} 

若要接收传入呼叫,应用程序需要向其 CallAgent 添加 CallAgentDelegate

let callAgentDelegate = ApplicationCallAgentDelegate() 
callClient.createCallAgent(userCredential: userCredential) { callAgent, error in 
    if error != nil { 
        // Raise the error to the user and return 
    } 
    self.callAgent = callAgent 
    self.callAgent?.delegate = callAgentDelegate 
} 

CallAgentDelegate 就位并与 CallAgent 实例关联后,应用程序应该能够接收传入呼叫。

接听来电

func acceptCall(incomingCall: IncomingCall) { 
    let options = AcceptCallOptions() 
    incomingCall.accept(options: options) {(call, error) in 
        if error != nil { 
            // Raise the error to the user and return 
        } 
      // The call is established clients can speak/view each other 
    }
} 

类引用

类名 说明
CallAgentDelegate 定义 ACSCallAgent 在响应重要事件时调用的一组方法。
IncomingCall 描述传入呼叫

视频流

启动和停止视频

Twilio

访问相机

使用 Twilio Video 时,向通话添加视频包括两个步骤:

  1. 访问相机
  2. 将视频轨道添加到 LocalVideoTrack 列表
let options = CameraSourceOptions { (builder) in
    // Set the CameraSource options here
}
camera = CameraSource(options: options, delegate: aCameraDelegate)
创建 LocalVideoTrack

CameraSource 在创建后,便可关联到 LocalVideoTrack

localVideoTrack = LocalVideoTrack(source: camera!, enabled: true, name: "Camera")
将 LocalVideoTrack 添加到呼叫

在连接时。 在呼叫连接时,可以通过将本地视频轨道传递给可使用 ConnectOptions`` 设置的 localVideo 轨道列表,从而将本地视频轨道添加到呼叫中。

let connectOptions = ConnectOptions(token: accessToken) { builder in
     builder.videoTracks =  [localVideoTrack!]
}

在现有聊天室中。 本地参与者可以通过 publishVideoTrack(_ : LocalVideoTrack) 方法发布本地视频轨道。

room.localParticipant.publishVideoTrack(localVideoTrack)

Azure 通信服务

访问相机

通过 DeviceManager 完成访问相机。 可以使用以下代码获取 DeviceManager 的实例。

self.callClient.getDeviceManager { (deviceManager, error) in
    if (error != nil) {
       // Display an error message to the user and exit the closure 
       return
    }
    self.deviceManager = deviceManager                 
}
创建 LocalVideoStream

deviceManager 提供对可用于创建 LocalVideoStream 实例的相机的访问权限。

LocalVideoStream(camera: deviceManager.cameras.first!)
添加 LocalVideoStream

连接时

localVideoStream 可以通过 StartCallOptionsOutgoingVideoOptions 添加到流。

var callOptions = StartCallOptions()
let outgoingVideoOptions = OutgoingVideoOptions()
outgoingVideoOptions.streams = [localVideoStream]

在通话中

可以通过调用以 LocalVideoStream 作为参数的 startVideo 方法启动视频流。

call.startVideo(stream: localVideoStream) { error in
    if error != ni {
     // Report the error to the user and return 
    }
}

类引用

类名 说明
DeviceManager 促进与设备的交互
LocalVideoStream 本地视频流信息
VideoDeviceInfo 有关视频设备的信息
调用 描述呼叫

呈现视频

Twilio

若要使用 Twilio Video 呈现视频,可将符合 VideoRenderer 协议的对象添加到 VideoTrack。 SDK 提供了可随时使用的 VideoRenderer,名为 VideoView,它是 UIView 的一个子类。

let videoView = VideoView(frame: CGRect.zero, delegate: nil)

创建 VideoView 的实例后,VideoTrack(本地或远程)有一种方法 addVideoRenderer,该方法可用于添加创建为呈现器的 videoView

localVideoTrack = LocalVideoTrack(source: camera, enabled: true, name: "Camera")
// Add renderer to video track for local preview
localVideoTrack!.addRenderer(videoView)

Azure 通信服务

若要使用 Azure 通信服务呼叫呈现视频,请创建 VideoStreamRenderer 并将 LocalVideoStreamRemoteVideoStream 作为参数传递。

 let previewRenderer = try VideoStreamRenderer(localVideoStream: localVideoStream)

创建的 VideoStreamRenderer 可用于创建 VideoStreamRendererView,后者呈现传递给 VideoStreamRenderer 的视频流。

let scalingMode = ScalingMode.fit
let options = CreateViewOptions(scalingMode:scalingMode)
let previewView = try previewRenderer.createView(withOptions:options)

类引用

类名 说明
ScalingMode 本地和远程视频缩放模式的枚举
CreateViewOptions 呈现视频时传递的选项
VideoStreamRenderer 用于视频呈现的呈现器
OutgoingVideoOptions 文档尚不可用
VideoStreamRendererView 用于呈现视频的视图

音频流

切换麦克风

Twilio

通过与麦克风关联的 LocalAudioTrack 对麦克风进行静音和取消静音。

麦克风静音

self.localAudioTrack.isEnabled =  false

麦克风取消静音

self.localAudioTrack.isEnabled =  true

Azure 通信服务

通过对 Call 对象调用 muteOutgoingAudiounmuteoutgoingAudio 可以实现静音和取消静音。

麦克风静音

'call': call.muteOutgoingAudio() { error in
    if error == nil {
        isMuted = true
    } else {
       // Display an error to the user
    }
}

麦克风取消静音

callBase.unmuteOutgoingAudio() { error in
    if error == nil {
        isMuted = true
    } else {
       // Display an error to the user
    }
}

事件侦听器

Twilio Video 和 Azure 通信服务呼叫 SDK 提出各种委托来侦听呼叫事件。

聊天室/呼叫事件

Twilio

RoomDelegate 允许客户端侦听与聊天室相关的事件。 它具有可针对以下事件调用的方法:

  • 客户端已连接或无法连接到聊天室
  • 客户端正在重新连接到聊天室或已重新连接
  • 远程参与者已连接、断开连接、重新连接到聊天室
  • 聊天室录制已开始或停止
  • 主导发言人更改

Azure 通信服务

Azure 通信服务呼叫 SDK 使 Call 对象能够合并各种事件侦听器,从而在调用属性发生更改时通知它们。 应单独订阅每个事件类型。 若要了解有关事件处理的详细信息,请参阅事件教程

  • 呼叫状态已更改。 这是报告连接事件的位置
  • 远程参与者列表已更新
  • 本地视频流已更新
  • 静音状态已改变

本地参与者事件

Twilio

Twilio 具有 LocalParticpant 委托,允许客户端接收有关以下事件的更新:

  • 本地参与者已发布或未能发布媒体轨道(音频、视频、数据)
  • 本地参与者的网络质量级别已更改

Azure 通信服务

Azure 通信服务呼叫 SDK 具有 CallAgent 委托,允许客户端侦听呼叫代理相关事件。 它在以下情况下收到通知:

  • 在传入呼叫或现有呼叫断开连接的情况下更新或创建呼叫时
  • 收到传入呼叫时

远程参与者事件

这两个 SDK 都提出一个远程参与者委托,允许客户端收到有关每个远程参与者发生的情况的通知。

Twilio

RemoteParticipantDelegate 处理以下事件。

  • 远程参与者已发布或取消发布媒体轨道(视频、音频、数据)
  • 本地参与者已订阅、无法订阅或取消订阅远程媒体轨道(视频、音频、数据)
  • 远程参与者网络质量已更改
  • 远程参与者更改了轨道发布的优先级
  • 远程参与者已打开/关闭其视频轨道

Azure 通信服务

RemoteParticipantDelegate 处理以下事件。

  • 远程参与者状态已更改
  • 远程参与者已静音或未静音
  • 远程参与者正在讲话
  • 远程参与者显示名称已更改
  • 远程参与者已添加或删除视频流

相机事件

Twilio

Twilio 提议 CameraSourceDelegate 向客户端通知与相机相关的以下事件:

  • 相机源已中断或已恢复(例如,将应用置于背景中时)
  • 相机源失败
  • 相机源报告系统压力

Azure 通信服务

Azure 通信服务通话建议 DeviceManagerDelegate。 它包含单个方法,在当前 DeviceManager 上添加或删除视频设备时通知客户端。

类引用

类名 说明
CallDelegate 通过调用 SDK 来响应重要事件而调用的一组方法。
CallAgentDelegate ACSCallAgent 在响应重要事件时调用的一组方法。
RemoteParticipantDelegate ACSRemoteParticipant 在响应重要事件时调用的一组方法。
DeviceManagerDelegate ACSDeviceManager 在响应重要事件时调用的一组方法。

结束呼叫

Twilio

结束呼叫(从聊天室断开连接)通过 room.disconnect() 方法完成。

Azure 通信服务

挂起呼叫通过 Call 对象的 hangUp 方法完成。

call.hangUp(options: HangUpOptions()) { error in
            if error != nil {
                print("ERROR: It was not possible to hang up the call.")
            }
            self.call = nil
 }

类引用

类名 说明
调用 ACSCall 在响应重要事件时调用的一组方法。
HangUp 选项 用于挂断呼叫的属性包类

Azure 通信服务呼叫的主要功能

主导发言人

注册主导发言人更新的第一步是从 Call 对象获取主导发言人功能的实例。 在该教程中了解有关主导发言人配置的详细信息。

let dominantSpeakersFeature = call.feature(Features.dominantSpeakers)

获取主导发言人功能的实例后,DominantSpeakersCallFeatureDelegate 可以附加到其中。

dominantSpeakersFeature.delegate = DominantSpeakersDelegate()
public class DominantSpeakersDelegate : DominantSpeakersCallFeatureDelegate {
    public func dominantSpeakersCallFeature(_ dominantSpeakersCallFeature: DominantSpeakersCallFeature, didChangeDominantSpeakers args: PropertyChangedEventArgs) {
        // When the list changes, get the timestamp of the last change and the current list of Dominant Speakers
        let dominantSpeakersInfo = dominantSpeakersCallFeature.dominantSpeakersInfo
        let timestamp = dominantSpeakersInfo.lastUpdatedAt
        let dominantSpeakersList = dominantSpeakersInfo.speakers
    }
}

媒体质量统计信息

为了帮助你了解通话期间的媒体质量,Azure 通信服务 SDK 提供媒体质量统计信息。 使用它可检查传入和传出呼叫指标的低级别音频、视频和屏幕共享质量指标。有关详细信息,请参阅媒体质量统计信息指南。

面向用户的诊断

Azure 通信服务呼叫 SDK 提供一项称为“面向用户的诊断 (UFD)”的功能,可让客户端仔细检查呼叫的各种属性,以确定潜在问题。 若要了解有关面向用户的诊断的详细信息,请参阅面向用户的诊断

重要

以下列表中所述的 Azure 通信服务呼叫 SDK 的某些功能在 Twilio Video SDK 中没有等效项。

举手

举手功能允许呼叫参与者举手或取消举手。

视频背景

添加视频背景 允许用户模糊视频流中的背景。

视频聚焦

聚焦 允许用户固定和取消固定视频。

先决条件

  1. Azure 帐户: 确保 Azure 帐户处于活动状态。 新用户可以在 Microsoft Azure 创建免费帐户。
  2. 通信服务资源:通过 Azure 门户设置通信服务资源并记下连接字符串。
  3. Azure CLI:按照说明在 Windows.上安装 Azure CLI
  4. 用户访问令牌:生成用户访问令牌以实例化呼叫客户端。 可以使用 Azure CLI 创建一个,如下所示:
az communication identity token issue --scope voip --connection-string "yourConnectionString"

有关详细信息,请参阅使用 Azure CLI 创建和管理访问令牌

对于 Teams 用户的视频通话:

UI 库

Azure 通信服务 UI 库简化了使用 Azure 通信服务创建现代通信用户界面的过程。 它提供一系列现成的 UI 组件,可轻松集成到应用程序中。

此开源预建控件集可让你使用 Fluent UI SDK 组件创建美观的设计,并开发高质量的音频/视频通信体验。 有关详细信息,请查看 Azure 通信服务 UI 库概述。 概述包括有关 Web 和移动平台的全面信息。

安装

若要开始从 Twilio Video 迁移,第一步是将适用于 Android 的 Azure 通信服务呼叫 SDK 安装到项目中。 Azure 通信服务呼叫 SDK 可以集成为 gradle 依赖项。

  1. 添加 azure-communication-calling 包
dependencies {
     ...
    implementation "com.azure.android:azure-communication-calling:<version>"
}
  1. 在应用程序清单中检查权限

确保应用程序的清单文件包含必要的权限,并做出所需的调整。

<manifest >
   <uses-feature android:name="android.hardware.camera" /> 
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>

向 SDK 进行身份验证

为了能够使用 Azure 通信服务呼叫 SDK,需要使用访问令牌进行身份验证。

Twilio

以下代码片段假定 Twilio 服务的有效访问令牌的可用性。

在 Twilio Video 中,访问令牌用于连接到聊天室。 通过将令牌传递给 ConnectOptions,可以创建用于创建或连接聊天室的选项。

ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken).build();
room = Video.connect(context, connectOptions, roomListener);

Azure 通信服务

以下代码片段需要有效的访问令牌来启动 CallClient

需要有效的令牌。 有关详细信息,请参阅创建和管理访问令牌

String userToken = "<USER_TOKEN>";
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(accessToken);

CallClient callClient = new CallClient();
callAgent = callClient.createCallAgent(getApplicationContext(), tokenCredential).get();

类引用

类名 说明
CallClient 用作呼叫 SDK 入口点的类。
CommunicationTokenCredential Azure 通信服务用户令牌凭据
CallAgent 负责代表经过身份验证的用户管理呼叫的类

发起传出呼叫

Twilio

Twilio Video 有 Room 的概念,如果用户 Bob 想要与客户端 Alice Bob 进行呼叫,可以创建聊天室,Alice 必须通过实现推送通知等功能来连接到聊天室。

当用户 Bob 或用户 Alice 想要创建或连接到聊天室时,并且他们具有有效的访问令牌时。 他们可以传递要创建或作为 ConnectOptions 参数进行连接的聊天室名称。

ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken)
    .roomName(roomName)
    .build()
room = Video.connect(context, connectOptions, roomListener);

Azure 通信服务

连接到呼叫

使用 Azure 通信服务呼叫 SDK 发起呼叫包括以下步骤:

CommunicationUserIdentifier 表示使用标识 SDK 或 REST API 创建的用户标识。 如果应用程序不使用 Microsoft Teams 互操作性或电话功能,则它是唯一使用的标识符。

  1. 创建 CommunicationUserIdentifier 数组
  2. 对之前创建的 CallAgent 调用 startCall 方法
ArrayList<CommunicationIdentifier> userCallee = new ArrayList<>();
participants.add(new CommunicationUserIdentifier(“<USER_ID>”));

Call call = callAgent.startCall(context, userCallee);

连接到 Teams 通话

使用外部标识

连接到 Teams 呼叫与连接到单人呼叫几乎完全相同。 客户端应用程序不使用 StartCallOptions,而是将 JoinCallOptionsTeamsMeetingLocator 一起使用。

可以使用图形 API 来检索 Teams 会议链接。 图形文档中详细介绍了检索过程。

JoinCallOptions options = new JoinCallOptions();
TeamsMeetingLinkLocator teamsMeetingLinkLocator = new TeamsMeetingLinkLocator(meetingLink);
Call call = callAgent.join(getApplicationContext(), teamsMeetingLinkLocator, joinCallOptions);

接受和加入通话

Twilio

Twilio Video 使用 Room 的概念。 不同的客户端可以通过加入同一聊天室来建立通信。 因此,接受和加入通话不是直接的替代方法。

Azure 通信服务

接收传入呼叫

若要接受呼叫,必须先将应用程序配置为接收传入呼叫。

注册推送通知并处理传入的推送通知

通话客户端可以选择接收推送通知以接收传入呼叫。 本指南介绍如何为 Azure 通信服务呼叫设置 APNS。

设置 CallAgentListener

Azure 通信服务呼叫 SDK 包括 IncomingCallListener。 在 CallAgent 实例上设置了 IncomingCallListener。 此侦听器定义 onIncomingCall(IncomingCall incomingCall) 方法,在传入呼叫到达时触发。

callAgent.addOnIncomingCallListener((incomingCall) -> {
     this.incomingCall = incomingCall;

     // Get incoming call ID  
     incomingCall.getId();

     // Get information about caller
     incomingCall.getCallerInfo();

     // CallEndReason is also a property of IncomingCall
      CallEndReason callEndReason = incomingCall.getCallEndReason();
});

通过实现 CallAgentListener 并将其与 CallAgent 实例关联,应用程序即可接收传入呼叫。

接受传入呼叫

incomingCall.accept(context);

类引用

类名 说明
IncomingCallListener 传入呼叫的功能接口。
IncomingCall 描述传入呼叫

视频流

启动和停止视频

Twilio

访问相机

使用 Twilio Video 时,向通话添加视频包括两个步骤:

  1. 访问相机
  2. 将视频轨道添加到 LocalVideoTrack 列表
 // Access the camera
CameraCapturer cameraCapturer = new Camera2Capturer(context, frontCameraId);

  // Create a video track
LocalVideoTrack videoTrack = LocalVideoTrack.create(context, true, cameraCapturer, LOCAL_VIDEO_TRACK_NAME);

  // The VideoTrack is enabled by default. It can be enabled or disabled if necessary
videoTrack.enable(true|false);

添加 LocalVideoTrack

在连接时。 通过将 LocalVideoTrack 传递给通过 ConnectOptions 设置的 LocalVideoTrack 列表来完成添加本地视频轨道。

 ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken)
    .roomName(roomName)
    .videoTracks(localVideoTracks)
}

在现有聊天室中,本地参与者可以通过 publishTrack(LocalVideoTrack localVideoTrack) 方法发布本地视频轨道

room.localParticipant.publishVideoTrack(localVideoTrack)

Azure 通信服务

访问相机

通过 DeviceManager 完成访问相机。 使用以下代码片段获取 DeviceManager 的实例。

DeviceManager deviceManager = callClient.getDeviceManager(getApplicationContext()).get();

deviceManager.getCameras();
创建 LocalVideoStream

DeviceManager 提供对允许创建 LocalVideoStream 实例的相机对象的访问权限。

VideoDeviceInfo camera = deviceManager.getCameras().get(0);
LocalVideoStream videoStream = new LocalVideoStream(camera, context);
添加 LocalVideoStream

在连接时LocalVideoStream 通过 StartCallOptions 的 OutgoingVideoOptions 添加到流中。

    StartCallOptions options = new StartCallOptions();
    LocalVideoStream[] videoStreams = new LocalVideoStream[1];
    videoStreams[0] = videoStream;
    VideoOptions videoOptions = new VideoOptions(videoStreams);
    options.setVideoOptions(videoOptions);

在通话中。 通过调用 startVideo 方法来启动视频流,该方法接受 LocalVideoStream 作为其参数。

call.startVideo(context, videoStream).get();

类引用

类名 说明
DeviceManager 促进与设备的交互
LocalVideoStream 本地视频流信息
VideoDeviceInfo 有关视频设备的信息
VideoOptions 视频选项的属性包类
调用 描述呼叫

呈现视频

Twilio

若要使用 Twilio Video 呈现视频,可将符合 VideoSink 的对象添加到 VideoTrack。 SDK 提供名为 VideoView 的预生成 VideoSink,它将 android.view.View 作为子类。

videoTrack.addSink(videoVideoView);

Azure 通信服务

若要使用 Azure 通信服务呼叫呈现视频,请实例化 VideoStreamRenderer 并将 LocalVideoStreamRemoteVideoStream 作为参数传递给其构造函数。

VideoStreamRenderer previewRenderer = new VideoStreamRenderer(remoteStream, context);
VideoStreamRendererView preview = previewRenderer.createView(new CreateViewOptions(ScalingMode.FIT));

类引用

类名 说明
ScalingMode 本地和远程视频缩放模式的枚举
CreateViewOptions 呈现视频时传递的选项
VideoStreamRenderer 用于视频呈现的呈现器
VideoStreamRendererView 用于呈现视频的视图

音频流

切换麦克风

Twilio

在 Twilio 视频 SDK 上,通过启用或禁用与麦克风关联的 LocalAudioTrack 来实现麦克风静音和取消静音。

localAudioTrack.enable(true|false);

Azure 通信服务

Call 对象提出用于静音和取消静音麦克风的方法。

call.muteOutgoingAudio(context).get();
call.unmuteOutgoingAudio(context).get();

// Mute incoming audio sets the call volume to 0. To mute or unmute the incoming audio, use the muteIncomingAudio and unmuteIncomingAudio asynchronous APIs

call.muteIncomingAudio(context).get();
call.unmuteIncomingAudio(context).get();

事件侦听器

Twilio Video 和 Azure 通信服务呼叫 SDK 提出各种侦听器来侦听呼叫事件。

聊天室/呼叫事件

Twilio

Room.Listener 允许客户端侦听与 Room 对象相关的事件。 Room.Listener 包括为以下事件触发的方法:

  • 客户端已连接或无法连接到聊天室
  • 客户端正在重新连接到聊天室或已重新连接
  • 远程参与者已连接、断开连接、重新连接到聊天室
  • 聊天室录制已开始或停止
  • 主导发言人已改变

Azure 通信服务

Azure 通信服务呼叫 SDK 使 Call 对象能够合并各种 PropertyChangedListener,从而在调用属性发生更改时通知它们。 应单独订阅每个事件类型。 若要了解有关事件处理的详细信息,请参阅事件教程

可分配给呼叫的各种 PropertyChangedListeners 包含 Twilio Room.Listener 涵盖的某些事件,对以下事件采用方法:

  • 呼叫状态已更改
  • 远程参与者列表已更新
  • 本地视频流已更新
  • 静音状态已改变

本地参与者事件

Twilio

Twilio 具有 LocalParticipant.Listener,允许客户端接收有关以下事件的更新:

  • 本地参与者已发布或未能发布媒体轨道(音频、视频、数据)。
  • 本地参与者的网络质量级别已更改。

Azure 通信服务

CallAgent 通过两个侦听器(CallsUpdatedListenerIncomingCallListener)接收有关呼叫的更新。 以下事件分别触发这些侦听器:

  • 调用已更新。 创建新的呼叫或现有呼叫断开连接。
  • 收到传入呼叫。

远程参与者事件

这两个 SDK 都提供了用于处理来自远程参与者的更新的机制。

Twilio

RemoteParticipant.Listener 处理以下事件。

  • 远程参与者已发布或取消发布媒体轨道(视频、音频、数据)
  • 本地参与者已订阅、无法订阅或取消订阅远程媒体轨道(视频、音频、数据)
  • 远程参与者网络质量已更改
  • 远程参与者更改了轨道发布的优先级
  • 远程参与者已打开/关闭其视频轨道

Azure 通信服务

PropertyChangedListener 添加到 RemoteParticipant 对象,以接收以下事件的更新:

  • 远程参与者状态已更改
  • 远程参与者已静音或未静音
  • 远程参与者正在讲话
  • 远程参与者显示名称已更改
  • 远程参与者已添加或删除视频流

相机事件

Twilio

Twilio 提议 CameraCapturer.Listener 向客户端通知与相机相关的以下事件:

  • 相机源已切换
  • 相机源失败
  • 第一帧已从相机捕获

Azure 通信服务

Azure 通信服务呼叫 SDK 提出 VideoDevicesUpdatedListener。 它定义在当前 DeviceManager 上添加或删除视频设备时通知客户端的单个方法。

类引用

类名 说明
PropertyChangedListener 告知库呼叫状态已更改
CallsUpdatedListener 在呼叫更新时告知库
IncomingCallListener 向库告知传入呼叫
VideoDevicesUpdatedListener 告知库已在当前库中添加或删除新视频设备

结束呼叫

Twilio

结束呼叫(从聊天室断开连接)通过 room.disconnect() 方法完成。

room.disconnect();

Azure 通信服务

挂起呼叫通过 Call 对象的 hangUp 方法完成。

call.hangUp().get();

// Set the 'forEveryone' property to true to end call for all participants
HangUpOptions options = new HangUpOptions();
options.setForEveryone(true);
call.hangUp(options).get();

类引用

类名 说明
HangUp 选项 用于挂断呼叫的属性包类

Azure 通信服务呼叫的主要功能

主导发言人

若要注册有关主导发言人的更新,请实例化 Call 对象中的 DominantSpeakersCallFeature。 在该教程中了解有关主导发言人配置的详细信息。

DominantSpeakersCallFeature dominantSpeakersFeature = call.feature(Features.DOMINANT_SPEAKERS);

// Subscribe to the dominant speaker change event to receive updates about the dominant speaker.

dominantSpeakersFeature.addOnDominantSpeakersChangedListener(event -> {
       dominantSpeakersFeature.getDominantSpeakersInfo();
});

媒体质量统计信息

为了帮助你了解通话期间的媒体质量,Azure 通信服务 SDK 提供媒体质量统计信息。 使用它可检查传入和传出呼叫指标的低级别音频、视频和屏幕共享质量指标。有关详细信息,请参阅媒体质量统计信息指南。

面向用户的诊断

Azure 通信服务呼叫 SDK 提供一项称为“面向用户的诊断 (UFD)”的功能,可让客户端仔细检查呼叫的各种属性,以确定潜在问题。 若要了解有关面向用户的诊断的详细信息,请参阅面向用户的诊断

重要

以下列表中所述的 Azure 通信服务呼叫 SDK 的某些功能在 Twilio Video SDK 中没有等效项。

举手

举手功能允许呼叫参与者举手或取消举手。

视频背景

添加视频背景 允许用户模糊视频流中的背景。

视频聚焦

聚焦 允许用户固定和取消固定视频。