Rychlý start: Přidání videohovorů 1:1 jako uživatele Teams do aplikace
Začněte se službou Azure Communication Services pomocí sady SDK pro volání komunikačních služeb a přidejte do aplikace hlasové volání 1:1 a videohovory. Dozvíte se, jak zahájit a přijmout volání pomocí sady SDK pro volání služeb Azure Communication Services pro JavaScript.
Ukázka kódu
Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.
Požadavky
- Získejte účet Azure s aktivním předplatným. Vytvoření účtu zdarma
- Potřebujete mít Node.js 18. Instalační program msi můžete použít k instalaci.
- Vytvořte aktivní prostředek komunikační služby. Vytvořte prostředek komunikační služby.
- Vytvořte přístupový token uživatele pro vytvoření instance klienta volání. Naučte se vytvářet a spravovat přístupové tokeny uživatelů.
- Získejte ID vlákna Teams pro operace volání pomocí Graph Exploreru. Přečtěte si další informace o tom, jak vytvořit ID vlákna chatu.
Nastavení
Vytvoření nové aplikace Node.js
Otevřete terminál nebo příkazové okno, vytvořte pro aplikaci nový adresář a přejděte do adresáře.
mkdir calling-quickstart && cd calling-quickstart
Spuštěním příkazu npm init -y
vytvořte soubor package.json s výchozím nastavením.
npm init -y
Nainstalujte balíček .
npm install
Pomocí příkazu nainstalujte sadu SDK pro volání služeb Azure Communication Services pro JavaScript.
Důležité
V tomto rychlém startu se používá nejnovější verze sady SDK pro volání služeb Azure Communication Services.
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Nastavení architektury aplikace
V tomto rychlém startu se ke sbalení prostředků aplikace používá webpack. Spuštěním následujícího příkazu nainstalujte webpack
balíčky a webpack-cli
npm a webpack-dev-server
uveďte je jako vývojové závislosti ve vaší package.json
:
npm install copy-webpack-plugin@^11.0.0 webpack@^5.88.2 webpack-cli@^5.1.4 webpack-dev-server@^4.15.1 --save-dev
Vytvořte index.html
soubor v kořenovém adresáři projektu. Tento soubor použijeme ke konfiguraci základního rozložení, které uživateli umožní umístit videohovor 1:1.
Tady je kód:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Azure Communication Services - Teams Calling Web Application</title>
</head>
<body>
<h4>Azure Communication Services - Teams Calling Web Application</h4>
<input id="user-access-token"
type="text"
placeholder="User access token"
style="margin-bottom:1em; width: 500px;"/>
<button id="initialize-teams-call-agent" type="button">Login</button>
<br>
<br>
<input id="callee-teams-user-id"
type="text"
placeholder="Microsoft Teams callee's id (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)"
style="margin-bottom:1em; width: 500px; display: block;"/>
<button id="start-call-button" type="button" disabled="true">Start Call</button>
<button id="hangup-call-button" type="button" disabled="true">Hang up Call</button>
<button id="accept-call-button" type="button" disabled="true">Accept Call</button>
<button id="start-video-button" type="button" disabled="true">Start Video</button>
<button id="stop-video-button" type="button" disabled="true">Stop Video</button>
<br>
<br>
<div id="connectedLabel" style="color: #13bb13;" hidden>Call is connected!</div>
<br>
<div id="remoteVideoContainer" style="width: 40%;" hidden>Remote participants' video streams:</div>
<br>
<div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div>
<!-- points to the bundle generated from client.js -->
<script src="./main.js"></script>
</body>
</html>
Objektový model webové sady SDK volání služeb Azure Communication Services
Následující třídy a rozhraní zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services:
Název | Popis |
---|---|
CallClient |
Hlavní vstupní bod do volající sady SDK. |
AzureCommunicationTokenCredential |
Implementuje CommunicationTokenCredential rozhraní, které se používá k vytvoření instance teamsCallAgent . |
TeamsCallAgent |
Používá se ke spouštění a správě hovorů v Teams. |
DeviceManager |
Slouží ke správě mediálních zařízení. |
TeamsCall |
Používá se k reprezentaci hovoru v Teams. |
LocalVideoStream |
Používá se k vytvoření místního videostreamu pro zařízení fotoaparátu v místním systému. |
RemoteParticipant |
Používá se pro reprezentaci vzdáleného účastníka v hovoru. |
RemoteVideoStream |
Používá se pro reprezentaci vzdáleného video streamu ze vzdáleného účastníka. |
Vytvořte soubor v kořenovém adresáři projektu, který bude index.js
obsahovat logiku aplikace pro účely tohoto rychlého startu. Do index.js přidejte následující kód:
// Make sure to install the necessary dependencies
const { CallClient, VideoStreamRenderer, LocalVideoStream } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential } = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the log level and output
setLogLevel('verbose');
AzureLogger.log = (...args) => {
console.log(...args);
};
// Calling web sdk objects
let teamsCallAgent;
let deviceManager;
let call;
let incomingCall;
let localVideoStream;
let localVideoStreamRenderer;
// UI widgets
let userAccessToken = document.getElementById('user-access-token');
let calleeTeamsUserId = document.getElementById('callee-teams-user-id');
let initializeCallAgentButton = document.getElementById('initialize-teams-call-agent');
let startCallButton = document.getElementById('start-call-button');
let hangUpCallButton = document.getElementById('hangup-call-button');
let acceptCallButton = document.getElementById('accept-call-button');
let startVideoButton = document.getElementById('start-video-button');
let stopVideoButton = document.getElementById('stop-video-button');
let connectedLabel = document.getElementById('connectedLabel');
let remoteVideoContainer = document.getElementById('remoteVideoContainer');
let localVideoContainer = document.getElementById('localVideoContainer');
/**
* Create an instance of CallClient. Initialize a TeamsCallAgent instance with a CommunicationUserCredential via created CallClient. TeamsCallAgent enables us to make outgoing calls and receive incoming calls.
* You can then use the CallClient.getDeviceManager() API instance to get the DeviceManager.
*/
initializeCallAgentButton.onclick = async () => {
try {
const callClient = new CallClient();
tokenCredential = new AzureCommunicationTokenCredential(userAccessToken.value.trim());
teamsCallAgent = await callClient.createTeamsCallAgent(tokenCredential)
// Set up a camera device to use.
deviceManager = await callClient.getDeviceManager();
await deviceManager.askDevicePermission({ video: true });
await deviceManager.askDevicePermission({ audio: true });
// Listen for an incoming call to accept.
teamsCallAgent.on('incomingCall', async (args) => {
try {
incomingCall = args.incomingCall;
acceptCallButton.disabled = false;
startCallButton.disabled = true;
} catch (error) {
console.error(error);
}
});
startCallButton.disabled = false;
initializeCallAgentButton.disabled = true;
} catch(error) {
console.error(error);
}
}
/**
* Place a 1:1 outgoing video call to a user
* Add an event listener to initiate a call when the `startCallButton` is selected.
* Enumerate local cameras using the deviceManager `getCameraList` API.
* In this quickstart, we're using the first camera in the collection. Once the desired camera is selected, a
* LocalVideoStream instance will be constructed and passed within `videoOptions` as an item within the
* localVideoStream array to the call method. When the call connects, your application will be sending a video stream to the other participant.
*/
startCallButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
call = teamsCallAgent.startCall({ microsoftTeamsUserId: calleeTeamsUserId.value.trim() }, { videoOptions: videoOptions });
// Subscribe to the call's properties and events.
subscribeToCall(call);
} catch (error) {
console.error(error);
}
}
/**
* Accepting an incoming call with a video
* Add an event listener to accept a call when the `acceptCallButton` is selected.
* You can accept incoming calls after subscribing to the `TeamsCallAgent.on('incomingCall')` event.
* You can pass the local video stream to accept the call with the following code.
*/
acceptCallButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
call = await incomingCall.accept({ videoOptions });
// Subscribe to the call's properties and events.
subscribeToCall(call);
} catch (error) {
console.error(error);
}
}
// Subscribe to a call obj.
// Listen for property changes and collection updates.
subscribeToCall = (call) => {
try {
// Inspect the initial call.id value.
console.log(`Call Id: ${call.id}`);
//Subscribe to call's 'idChanged' event for value changes.
call.on('idChanged', () => {
console.log(`Call ID changed: ${call.id}`);
});
// Inspect the initial call.state value.
console.log(`Call state: ${call.state}`);
// Subscribe to call's 'stateChanged' event for value changes.
call.on('stateChanged', async () => {
console.log(`Call state changed: ${call.state}`);
if(call.state === 'Connected') {
connectedLabel.hidden = false;
acceptCallButton.disabled = true;
startCallButton.disabled = true;
hangUpCallButton.disabled = false;
startVideoButton.disabled = false;
stopVideoButton.disabled = false;
} else if (call.state === 'Disconnected') {
connectedLabel.hidden = true;
startCallButton.disabled = false;
hangUpCallButton.disabled = true;
startVideoButton.disabled = true;
stopVideoButton.disabled = true;
console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`);
}
});
call.on('isLocalVideoStartedChanged', () => {
console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`);
});
console.log(`isLocalVideoStarted: ${call.isLocalVideoStarted}`);
call.localVideoStreams.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
call.on('localVideoStreamsUpdated', e => {
e.added.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
e.removed.forEach(lvs => {
removeLocalVideoStream();
});
});
// Inspect the call's current remote participants and subscribe to them.
call.remoteParticipants.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant);
});
// Subscribe to the call's 'remoteParticipantsUpdated' event to be
// notified when new participants are added to the call or removed from the call.
call.on('remoteParticipantsUpdated', e => {
// Subscribe to new remote participants that are added to the call.
e.added.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant)
});
// Unsubscribe from participants that are removed from the call
e.removed.forEach(remoteParticipant => {
console.log('Remote participant removed from the call.');
});
});
} catch (error) {
console.error(error);
}
}
// Subscribe to a remote participant obj.
// Listen for property changes and collection updates.
subscribeToRemoteParticipant = (remoteParticipant) => {
try {
// Inspect the initial remoteParticipant.state value.
console.log(`Remote participant state: ${remoteParticipant.state}`);
// Subscribe to remoteParticipant's 'stateChanged' event for value changes.
remoteParticipant.on('stateChanged', () => {
console.log(`Remote participant state changed: ${remoteParticipant.state}`);
});
// Inspect the remoteParticipants's current videoStreams and subscribe to them.
remoteParticipant.videoStreams.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be
// notified when the remoteParticipant adds new videoStreams and removes video streams.
remoteParticipant.on('videoStreamsUpdated', e => {
// Subscribe to newly added remote participant's video streams.
e.added.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Unsubscribe from newly removed remote participants' video streams.
e.removed.forEach(remoteVideoStream => {
console.log('Remote participant video stream was removed.');
})
});
} catch (error) {
console.error(error);
}
}
/**
* Subscribe to a remote participant's remote video stream obj.
* You have to subscribe to the 'isAvailableChanged' event to render the remoteVideoStream. If the 'isAvailable' property
* changes to 'true' a remote participant is sending a stream. Whenever the availability of a remote stream changes
* you can choose to destroy the whole 'Renderer' a specific 'RendererView' or keep them. Displaying RendererView without a video stream will result in a blank video frame.
*/
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
// Create a video stream renderer for the remote video stream.
let videoStreamRenderer = new VideoStreamRenderer(remoteVideoStream);
let view;
const renderVideo = async () => {
try {
// Create a renderer view for the remote video stream.
view = await videoStreamRenderer.createView();
// Attach the renderer view to the UI.
remoteVideoContainer.hidden = false;
remoteVideoContainer.appendChild(view.target);
} catch (e) {
console.warn(`Failed to createView, reason=${e.message}, code=${e.code}`);
}
}
remoteVideoStream.on('isAvailableChanged', async () => {
// Participant has switched video on.
if (remoteVideoStream.isAvailable) {
await renderVideo();
// Participant has switched video off.
} else {
if (view) {
view.dispose();
view = undefined;
}
}
});
// Participant has video on initially.
if (remoteVideoStream.isAvailable) {
await renderVideo();
}
}
// Start your local video stream.
// This will send your local video stream to remote participants so they can view it.
startVideoButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
await call.startVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
// Stop your local video stream.
// This will stop your local video stream from being sent to remote participants.
stopVideoButton.onclick = async () => {
try {
await call.stopVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
/**
* To render a LocalVideoStream, you need to create a new instance of VideoStreamRenderer, and then
* create a new VideoStreamRendererView instance using the asynchronous createView() method.
* You may then attach view.target to any UI element.
*/
// Create a local video stream for your camera device
createLocalVideoStream = async () => {
const camera = (await deviceManager.getCameras())[0];
if (camera) {
return new LocalVideoStream(camera);
} else {
console.error(`No camera device found on the system`);
}
}
// Display your local video stream preview in your UI
displayLocalVideoStream = async () => {
try {
localVideoStreamRenderer = new VideoStreamRenderer(localVideoStream);
const view = await localVideoStreamRenderer.createView();
localVideoContainer.hidden = false;
localVideoContainer.appendChild(view.target);
} catch (error) {
console.error(error);
}
}
// Remove your local video stream preview from your UI
removeLocalVideoStream = async() => {
try {
localVideoStreamRenderer.dispose();
localVideoContainer.hidden = true;
} catch (error) {
console.error(error);
}
}
// End the current call
hangUpCallButton.addEventListener("click", async () => {
// end the current call
await call.hangUp();
});
Přidání kódu místního serveru webpacku
V kořenovém adresáři projektu vytvořte soubor s názvem webpack.config.js , který bude obsahovat logiku místního serveru pro účely tohoto rychlého startu. Do webpack.config.js přidejte následující kód:
const path = require('path');
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: {
directory: path.join(__dirname, './')
},
},
plugins: [
new CopyPlugin({
patterns: [
'./index.html'
]
}),
]
};
Spuštění kódu
Použijte k webpack-dev-server
sestavení a spuštění aplikace. Spuštěním následujícího příkazu sbalte hostitele aplikace v místním webovém serveru:
`npx webpack serve --config webpack.config.js`
Otevřete prohlížeč a na dvou kartách přejděte na http://localhost:8080/. Karty by měly zobrazit podobný výsledek jako na následujícím obrázku:
Na první kartě zadejte platný přístupový token uživatele. Na druhé kartě zadejte jiný platný přístupový token uživatele. Pokud ještě nemáte k dispozici přístupové tokeny, projděte si dokumentaci k přístupovým tokenům uživatele. Na oboukartách Karty by měly zobrazit podobný výsledek jako na následujícím obrázku:
Na první kartě zadejte identitu uživatele Azure Communication Services na druhé kartě a vyberte tlačítko Zahájit hovor. První karta spustí odchozí hovor na druhou kartu a tlačítko Přijmout hovor druhé karty se aktivuje:
Na druhé kartě vyberte tlačítko Přijmout hovor. Hovor bude zodpovězen a připojen. Karty by měly zobrazit podobný výsledek jako na následujícím obrázku:
Obě karty jsou nyní úspěšně ve videohovoru 1:1. Oba uživatelé můžou slyšet zvuk sebe navzájem a vidět si navzájem stream videa.
Začněte se službou Azure Communication Services pomocí sady SDK pro volání komunikačních služeb a přidejte do aplikace hlasové volání 1:1 a videohovory. Naučíte se, jak zahájit a přijmout volání pomocí sady SDK pro volání služeb Azure Communication Services pro Windows.
Ukázka kódu
Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.
Požadavky
Pro absolvování tohoto kurzu musí být splněné následující požadavky:
- Účet Azure s aktivním předplatným. Vytvoření účtu zdarma
- Nainstalujte sadu Visual Studio 2022 s Univerzální platforma Windows úlohou vývoje.
- Nasazený prostředek komunikační služby. Vytvořte prostředek komunikační služby. Pro účely tohoto rychlého startu musíte zaznamenat připojovací řetězec.
- Přístupový token uživatele pro vaši službu Azure Communication Service.
- Získejte ID vlákna Teams pro operace volání pomocí Graph Exploreru. Přečtěte si další informace o tom, jak vytvořit ID vlákna chatu.
Nastavení
Vytvoření projektu
V sadě Visual Studio vytvořte nový projekt pomocí šablony Prázdná aplikace (Univerzální windows) pro nastavení jednostránka Univerzální platforma Windows (UPW).
Nainstalujte balíček .
Vyberte projekt pravým tlačítkem a přejděte na Manage Nuget Packages
instalaci Azure.Communication.Calling.WindowsClient
verze 1.2.0-beta.1 nebo superior. Ujistěte se, že je zaškrtnuté políčko Zahrnout předběžné verze.
Vyžádat si přístup
Přejděte na Package.appxmanifest
a vyberte Capabilities
.
Zkontrolujte a Internet (Client & Server)
získejte Internet (Client)
příchozí a odchozí přístup k internetu. Zkontrolujte Microphone
, jestli chcete získat přístup ke zvukovému kanálu mikrofonu a Webcam
získat přístup k videokamerě.
Nastavení architektury aplikace
Potřebujeme nakonfigurovat základní rozložení pro připojení naší logiky. Abychom mohli umístit odchozí hovor, musíme TextBox
zadat ID uživatele volaného. Potřebujeme Start/Join call
také tlačítko a Hang up
tlačítko. Součástí Mute
této ukázky jsou také zaškrtávací políčka, BackgroundBlur
které demonstrují funkce přepínání zvukových stavů a efektů videa.
MainPage.xaml
Otevřete projekt a přidejte uzel Grid
do svéhoPage
:
<Page
x:Class="CallingQuickstart.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CallingQuickstart"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Width="800" Height="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="16*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="200*"/>
<RowDefinition Height="60*"/>
<RowDefinition Height="16*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="10,10,10,10" />
<Grid x:Name="AppTitleBar" Background="LightSeaGreen">
<TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="7,7,0,0"/>
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<MediaPlayerElement x:Name="LocalVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="0" VerticalAlignment="Center" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="1" VerticalAlignment="Center" AutoPlay="True" />
</Grid>
<StackPanel Grid.Row="3" Orientation="Vertical" Grid.RowSpan="2">
<StackPanel Orientation="Horizontal">
<Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
<Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
<CheckBox x:Name="MuteLocal" Content="Mute" Margin="10,0,0,0" Click="MuteLocal_Click" Width="74"/>
</StackPanel>
</StackPanel>
<TextBox Grid.Row="5" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
</Grid>
</Page>
MainPage.xaml.cs
Otevřete a nahraďte obsah následující implementací:
using Azure.Communication.Calling.WindowsClient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Media.Core;
using Windows.Networking.PushNotifications;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace CallingQuickstart
{
public sealed partial class MainPage : Page
{
private const string authToken = "<AUTHENTICATION_TOKEN>";
private CallClient callClient;
private CallTokenRefreshOptions callTokenRefreshOptions = new CallTokenRefreshOptions(false);
private TeamsCallAgent teamsCallAgent;
private TeamsCommunicationCall teamsCall;
private LocalOutgoingAudioStream micStream;
private LocalOutgoingVideoStream cameraStream;
#region Page initialization
public MainPage()
{
this.InitializeComponent();
// Additional UI customization code goes here
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}
#endregion
#region UI event handlers
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start a call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// Hang up a call
}
private async void MuteLocal_Click(object sender, RoutedEventArgs e)
{
// Toggle mute/unmute audio state of a call
}
#endregion
#region API event handlers
private async void OnIncomingCallAsync(object sender, TeamsIncomingCallReceivedEventArgs args)
{
// Handle incoming call event
}
private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
{
// Handle connected and disconnected state change of a call
}
#endregion
}
}
Objektový model
V další tabulce jsou uvedené třídy a rozhraní, které zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services:
Název | Popis |
---|---|
CallClient |
Jedná se CallClient o hlavní vstupní bod volající sady SDK. |
TeamsCallAgent |
Slouží TeamsCallAgent ke spouštění a správě hovorů. |
TeamsCommunicationCall |
Slouží TeamsCommunicationCall ke správě probíhajícího hovoru. |
CallTokenCredential |
Slouží CallTokenCredential jako přihlašovací údaje tokenu k vytvoření instance TeamsCallAgent . |
CallIdentifier |
Slouží CallIdentifier k reprezentaci identity uživatele, což může být jedna z následujících možností: MicrosoftTeamsUserCallIdentifier , UserCallIdentifier atd PhoneNumberCallIdentifier . |
Ověření klienta
Inicializace TeamsCallAgent
instance pomocí přístupového tokenu uživatele, který nám umožňuje provádět a přijímat volání, a volitelně získat instanci DeviceManager pro dotazování na konfigurace klientských zařízení.
V kódu nahraďte <AUTHENTICATION_TOKEN>
přístupovým tokenem uživatele. Pokud ještě token nemáte k dispozici, projděte si dokumentaci k přístupovým tokenům uživatele.
Přidejte InitCallAgentAndDeviceManagerAsync
funkci, která spouští sadu SDK. Tento pomocník je možné přizpůsobit tak, aby splňoval požadavky vaší aplikace.
private async Task InitCallAgentAndDeviceManagerAsync()
{
this.callClient = new CallClient(new CallClientOptions() {
Diagnostics = new CallDiagnosticsOptions() {
AppName = "CallingQuickstart",
AppVersion="1.0",
Tags = new[] { "Calling", "CTE", "Windows" }
}
});
// Set up local video stream using the first camera enumerated
var deviceManager = await this.callClient.GetDeviceManagerAsync();
var camera = deviceManager?.Cameras?.FirstOrDefault();
var mic = deviceManager?.Microphones?.FirstOrDefault();
micStream = new LocalOutgoingAudioStream();
var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);
this.teamsCallAgent = await this.callClient.CreateTeamsCallAgentAsync(tokenCredential);
this.teamsCallAgent.IncomingCallReceived += OnIncomingCallAsync;
}
Zahájení hovoru
Přidejte implementaci ke CallButton_Click
spuštění různých druhů volání s objektem teamsCallAgent
, který jsme vytvořili, a připojte RemoteParticipantsUpdated
StateChanged
obslužné rutiny událostí k objektu TeamsCommunicationCall
.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
teamsCall = await StartCteCallAsync(callString);
if (teamsCall != null)
{
teamsCall.StateChanged += OnStateChangedAsync;
}
}
Ukončení hovoru
Ukončení aktuálního hovoru po kliknutí na Hang up
tlačítko Přidejte implementaci do HangupButton_Click pro ukončení volání a zastavte streamy náhledu a videa.
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
var teamsCall = this.teamsCallAgent?.Calls?.FirstOrDefault();
if (teamsCall != null)
{
await teamsCall.HangUpAsync(new HangUpOptions() { ForEveryone = false });
}
}
Přepnutí ztlumení nebo zrušení ztlumení zvuku
Po kliknutí na Mute
tlačítko ztlumte odchozí zvuk. Přidejte implementaci do MuteLocal_Click pro ztlumení volání.
private async void MuteLocal_Click(object sender, RoutedEventArgs e)
{
var muteCheckbox = sender as CheckBox;
if (muteCheckbox != null)
{
var teamsCall = this.teamsCallAgent?.Calls?.FirstOrDefault();
if (teamsCall != null)
{
if ((bool)muteCheckbox.IsChecked)
{
await teamsCall.MuteOutgoingAudioAsync();
}
else
{
await teamsCall.UnmuteOutgoingAudioAsync();
}
}
// Update the UI to reflect the state
}
}
Zahájení hovoru
Jakmile se StartTeamsCallOptions
objekt získá, TeamsCallAgent
můžete ho použít k zahájení volání Teams:
private async Task<TeamsCommunicationCall> StartCteCallAsync(string cteCallee)
{
var options = new StartTeamsCallOptions();
var teamsCall = await this.teamsCallAgent.StartCallAsync( new MicrosoftTeamsUserCallIdentifier(cteCallee), options);
return call;
}
Přijetí příchozího hovoru
TeamsIncomingCallReceived
Jímka událostí je nastavena v pomocné rutině InitCallAgentAndDeviceManagerAsync
sdk bootstrap .
this.teamsCallAgent.IncomingCallReceived += OnIncomingCallAsync;
Aplikace má příležitost ke konfiguraci způsobu přijetí příchozího hovoru, jako jsou druhy video a zvukového streamu.
private async void OnIncomingCallAsync(object sender, TeamsIncomingCallReceivedEventArgs args)
{
var teamsIncomingCall = args.IncomingCall;
var acceptteamsCallOptions = new AcceptTeamsCallOptions() { };
teamsCall = await teamsIncomingCall.AcceptAsync(acceptteamsCallOptions);
teamsCall.StateChanged += OnStateChangedAsync;
}
Připojení k hovoru v Teams
Uživatel se také může připojit k existujícímu volání předáním odkazu.
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
JoinTeamsCallOptions options = new JoinTeamsCallOptions();
TeamsCall call = await teamsCallAgent.JoinAsync(link, options);
Monitorování a reakce na událost změny stavu volání
StateChanged
událost objektu TeamsCommunicationCall
se aktivuje, když probíhající volání transakcí z jednoho stavu do druhého. Aplikace nabízí příležitosti k vyjádření změn stavu v uživatelském rozhraní nebo vložení obchodní logiky.
private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
{
var teamsCall = sender as TeamsCommunicationCall;
if (teamsCall != null)
{
var state = teamsCall.State;
// Update the UI
switch (state)
{
case CallState.Connected:
{
await teamsCall.StartAudioAsync(micStream);
break;
}
case CallState.Disconnected:
{
teamsCall.StateChanged -= OnStateChangedAsync;
teamsCall.Dispose();
break;
}
default: break;
}
}
}
Spuštění kódu
Kód můžete sestavit a spustit v sadě Visual Studio. Pro platformy řešení podporujeme ARM64
x64
a x86
.
Odchozí volání můžete provést tak, že do textového pole zadáte ID uživatele a kliknete na Start Call/Join
tlačítko. Volání 8:echo123
vás spojí s robotem echo, tato funkce je skvělá pro začátek a ověření fungování zvukových zařízení.
Začněte se službou Azure Communication Services pomocí sady SDK pro volání komunikačních služeb a přidejte do aplikace hlasové volání 1:1 a videohovory. Dozvíte se, jak zahájit a přijmout volání pomocí sady SDK pro volání služeb Azure Communication Services pro Javu.
Ukázka kódu
Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.
Požadavky
- Účet Azure s aktivním předplatným. Vytvoření účtu zdarma
- Android Studio pro vytvoření aplikace pro Android
- Nasazený prostředek komunikační služby. Vytvořte prostředek komunikační služby. Pro účely tohoto rychlého startu musíte zaznamenat připojovací řetězec.
- Přístupový token uživatele pro vaši službu Azure Communication Service.
- Získejte ID vlákna Teams pro operace volání pomocí Graph Exploreru. Přečtěte si další informace o tom, jak vytvořit ID vlákna chatu.
Nastavení
Vytvoření aplikace pro Android s prázdnou aktivitou
V Android Studiu vyberte Spustit nový projekt Android Studio.
V části Telefon a tablet vyberte šablonu projektu Prázdná aktivita.
Vyberte minimální sadu SDK rozhraní API 26: Android 8.0 (Oreo) nebo vyšší.
Nainstalujte balíček .
Vyhledejte soubor build.gradle na úrovni projektu a nezapomeňte ho přidat mavenCentral()
do seznamu úložišť v části buildscript
a allprojects
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Potom na úrovni modulu build.gradle přidejte následující řádky do oddílů závislostí a androidu.
android {
...
packagingOptions {
pickFirst 'META-INF/*'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0-beta.8'
...
}
Přidání oprávnění k manifestu aplikace
Aby bylo možné požádat o oprávnění požadovaná k volání, musí být deklarována v manifestu aplikace (app/src/main/AndroidManifest.xml
). Obsah souboru nahraďte následujícím kódem:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.contoso.ctequickstart">
<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" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--Our Calling SDK depends on the Apache HTTP SDK.
When targeting Android SDK 28+, this library needs to be explicitly referenced.
See https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Nastavení rozložení aplikace
Jsou potřeba dva vstupy: textový vstup pro volané ID a tlačítko pro umístění hovoru. Tyto vstupy lze přidat prostřednictvím návrháře nebo úpravou xml rozložení. Vytvořte tlačítko s ID call_button
a textovým vstupem callee_id
. Přejděte na (app/src/main/res/layout/activity_main.xml
) a nahraďte obsah souboru následujícím kódem:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/call_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Call"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="@+id/callee_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Callee Id"
android:inputType="textPersonName"
app:layout_constraintBottom_toTopOf="@+id/call_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Vytvoření generování uživatelského rozhraní a vazeb hlavní aktivity
S rozložením vytvořeným vazbami je možné přidat i základní generování aktivity. Aktivita zpracovává žádosti o oprávnění modulu runtime, vytvoření agenta volání týmů a umístění hovoru při stisknutí tlačítka. Každý z nich je popsaný ve své vlastní části. Metoda onCreate
je přepsána k vyvolání getAllPermissions
a createTeamsAgent
přidání vazeb pro tlačítko volání. K této události dochází pouze jednou při vytvoření aktivity. Další informace onCreate
najdete v příručce Principy životního cyklu aktivity.
Přejděte na MainActivity.java a nahraďte obsah následujícím kódem:
package com.contoso.ctequickstart;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.media.AudioManager;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.azure.android.communication.common.CommunicationUserIdentifier;
import com.azure.android.communication.common.CommunicationTokenCredential;
import com.azure.android.communication.calling.TeamsCallAgent;
import com.azure.android.communication.calling.CallClient;
import com.azure.android.communication.calling.StartTeamsCallOptions;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private TeamsCallAgent teamsCallAgent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getAllPermissions();
createTeamsAgent();
// Bind call button to call `startCall`
Button callButton = findViewById(R.id.call_button);
callButton.setOnClickListener(l -> startCall());
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
/**
* Request each required permission if the app doesn't already have it.
*/
private void getAllPermissions() {
// See section on requesting permissions
}
/**
* Create the call agent for placing calls
*/
private void createTeamsAgent() {
// See section on creating the call agent
}
/**
* Place a call to the callee id provided in `callee_id` text input.
*/
private void startCall() {
// See section on starting the call
}
}
Vyžádání oprávnění za běhu
Pro Android 6.0 a vyšší (úroveň rozhraní API 23) a targetSdkVersion
23 nebo vyšší jsou oprávnění udělena za běhu místo instalace aplikace. Aby bylo možné ho podporovat, getAllPermissions
je možné implementovat volání ActivityCompat.checkSelfPermission
a ActivityCompat.requestPermissions
pro každé požadované oprávnění.
/**
* Request each required permission if the app doesn't already have it.
*/
private void getAllPermissions() {
String[] requiredPermissions = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE};
ArrayList<String> permissionsToAskFor = new ArrayList<>();
for (String permission : requiredPermissions) {
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsToAskFor.add(permission);
}
}
if (!permissionsToAskFor.isEmpty()) {
ActivityCompat.requestPermissions(this, permissionsToAskFor.toArray(new String[0]), 1);
}
}
Poznámka:
Při návrhu aplikace zvažte, kdy by se tato oprávnění měla požadovat. Oprávnění by se měla vyžadovat podle potřeby, a ne předem. Další informace najdete v průvodci oprávněními androidu .
Objektový model
Následující třídy a rozhraní zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services:
Název | Popis |
---|---|
CallClient |
Jedná se CallClient o hlavní vstupní bod volající sady SDK. |
TeamsCallAgent |
Slouží TeamsCallAgent ke spouštění a správě hovorů. |
TeamsCall |
Používá TeamsCall se k reprezentaci hovoru v Teams. |
CommunicationTokenCredential |
Slouží CommunicationTokenCredential jako přihlašovací údaje tokenu k vytvoření instance TeamsCallAgent . |
CommunicationIdentifier |
Používá CommunicationIdentifier se jako jiný typ účastníka, který může být součástí hovoru. |
Vytvoření agenta z přístupového tokenu uživatele
Pomocí tokenu uživatele je možné vytvořit instanci ověřeného agenta volání. Obecně platí, že tento token se generuje ze služby s ověřováním specifickým pro aplikaci. Další informace o přístupových tokenech uživatelů najdete v průvodci uživatelskými přístupovými tokeny .
V tomto rychlém startu nahraďte <User_Access_Token>
přístupovým tokenem uživatele vygenerovaným pro prostředek služby Azure Communication Service.
/**
* Create the teams call agent for placing calls
*/
private void createAgent() {
String userToken = "<User_Access_Token>";
try {
CommunicationTokenCredential credential = new CommunicationTokenCredential(userToken);
teamsCallAgent = new CallClient().createTeamsCallAgent(getApplicationContext(), credential).get();
} catch (Exception ex) {
Toast.makeText(getApplicationContext(), "Failed to create teams call agent.", Toast.LENGTH_SHORT).show();
}
}
Spuštění hovoru pomocí agenta volání
Volání je možné provést přes agenta hovoru v teams a vyžaduje jenom zadání seznamu ID volaných a možností volání. Pro rychlý start se použijí výchozí možnosti volání bez videa a jedno volané ID z textového vstupu.
/**
* Place a call to the callee id provided in `callee_id` text input.
*/
private void startCall() {
EditText calleeIdView = findViewById(R.id.callee_id);
String calleeId = calleeIdView.getText().toString();
StartTeamsCallOptions options = new StartTeamsCallOptions();
teamsCallAgent.startCall(
getApplicationContext(),
new MicrosoftTeamsUserCallIdentifier(calleeId),
options);
}
Přijmout hovor
Přijetí hovoru je možné provést pomocí agenta volání teams pouze pomocí odkazu na aktuální kontext.
public void acceptACall(TeamsIncomingCall teamsIncomingCall){
teamsIncomingCall.accept(this);
}
Připojení k hovoru v Teams
Uživatel se může připojit k existujícímu volání předáním odkazu.
/**
* Join a call using a teams meeting link.
*/
public TeamsCall joinTeamsCall(TeamsCallAgent teamsCallAgent){
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
TeamsCall call = teamsCallAgent.join(this, link);
}
Připojení k hovoru v Teams s možnostmi
Můžeme se také připojit k existujícímu volání s přednastavenými možnostmi, jako je ztlumený.
/**
* Join a call using a teams meeting link while muted.
*/
public TeamsCall joinTeamsCall(TeamsCallAgent teamsCallAgent){
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
OutgoingAudioOptions audioOptions = new OutgoingAudioOptions().setMuted(true);
JoinTeamsCallOptions options = new JoinTeamsCallOptions().setAudioOptions(audioOptions);
TeamsCall call = teamsCallAgent.join(this, link, options);
}
Nastavení naslouchacího procesu příchozího volání
Aby bylo možné rozpoznat příchozí hovory a další akce, které tento uživatel neprováděje, musí být naslouchací procesy nastaveny.
private TeamsIncomingCall teamsincomingCall;
teamsCallAgent.addOnIncomingCallListener(this::handleIncomingCall);
private void handleIncomingCall(TeamsIncomingCall incomingCall) {
this.teamsincomingCall = incomingCall;
}
Spusťte aplikaci a zavolejte robota echo.
Aplikaci teď můžete spustit pomocí tlačítka Spustit aplikaci na panelu nástrojů (Shift+F10). Ověřte, že můžete volat.8:echo123
Přehraje se předem zaznamenaná zpráva a pak zprávu znovu zopakuje.
Začněte se službou Azure Communication Services tím, že pomocí sady SDK pro volání komunikačních služeb přidáte do své aplikace jednu službu pro videohovory. Naučíte se, jak zahájit a přijmout videohovor pomocí sady SDK pro volání služeb Azure Communication Services pro iOS pomocí identity Teams.
Ukázka kódu
Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.
Požadavky
- Získejte účet Azure s aktivním předplatným. Vytvoření účtu zdarma
- Mac se systémem Xcode spolu s platným certifikátem vývojáře nainstalovaným do klíčenky.
- Vytvořte aktivní prostředek komunikační služby. Vytvořte prostředek komunikační služby. Pro účely tohoto rychlého startu musíte zaznamenat připojovací řetězec.
- Přístupový token uživatele pro vaši službu Azure Communication Service.
- Získejte ID vlákna Teams pro operace volání pomocí Graph Exploreru. Přečtěte si další informace o tom, jak vytvořit ID vlákna chatu.
Nastavení
Vytvoření projektu Xcode
V Xcode vytvořte nový projekt pro iOS a vyberte šablonu aplikace s jedním zobrazením. Tento kurz používá architekturu SwiftUI, takže byste měli nastavit jazyk na Swift a uživatelské rozhraní na SwiftUI. Během tohoto rychlého startu nebudete vytvářet testy. Nebojte se zrušit zaškrtnutí políčka Zahrnout testy.
Instalace CocoaPods
Tento průvodce použijte k instalaci CocoaPods na Mac.
Instalace balíčku a závislostí pomocí CocoaPods
Pokud chcete vytvořit
Podfile
aplikaci, otevřete terminál a přejděte do složky projektu a spusťte inicializaci podu.Do souboru a uložte následující kód
Podfile
. Viz verze podpory sady SDK.
platform :ios, '13.0'
use_frameworks!
target 'VideoCallingQuickstart' do
pod 'AzureCommunicationCalling', '~> 2.10.0'
end
Spusťte instalaci podu.
Otevřete soubor
.xcworkspace
xcode.
Žádost o přístup k mikrofonu a fotoaparátu
Pokud chcete získat přístup k mikrofonu a fotoaparátu zařízení, musíte aktualizovat seznam vlastností informací aplikace pomocí a NSMicrophoneUsageDescription
NSCameraUsageDescription
. Přidruženou hodnotu nastavíte na řetězec, který obsahuje dialogové okno, které systém používá k vyžádání přístupu od uživatele.
Klikněte pravým tlačítkem myši na Info.plist
položku stromu projektu a vyberte Otevřít jako > zdrojový kód. Přidejte následující řádky oddílu nejvyšší úrovně <dict>
a pak soubor uložte.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for video calling</string>
Nastavení architektury aplikace
Otevřete soubor projektu ContentView.swift
a přidejte deklaraci importu do horní části souboru pro import AzureCommunicationCalling
knihovny a AVFoundation
. AvFoundation se používá k zachycení zvukového oprávnění z kódu.
import AzureCommunicationCalling
import AVFoundation
Objektový model
Následující třídy a rozhraní zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services pro iOS.
Název | Popis |
---|---|
CallClient |
Jedná se CallClient o hlavní vstupní bod volající sady SDK. |
TeamsCallAgent |
Slouží TeamsCallAgent ke spouštění a správě hovorů. |
TeamsIncomingCall |
Slouží TeamsIncomingCall k přijetí nebo odmítnutí příchozího hovoru v týmech. |
CommunicationTokenCredential |
Slouží CommunicationTokenCredential jako přihlašovací údaje tokenu k vytvoření instance TeamsCallAgent . |
CommunicationIdentifier |
Slouží CommunicationIdentifier k reprezentaci identity uživatele, což může být jedna z následujících možností: CommunicationUserIdentifier nebo PhoneNumberIdentifier CallingApplication . |
Vytvoření agenta volání v Teams
Nahraďte implementaci ContentView struct
některými jednoduchými ovládacími prvky uživatelského rozhraní, které uživateli umožňují zahájit a ukončit volání. Do těchto ovládacích prvků v tomto rychlém startu přidáme obchodní logiku.
struct ContentView: View {
@State var callee: String = ""
@State var callClient: CallClient?
@State var teamsCallAgent: TeamsCallAgent?
@State var teamsCall: TeamsCall?
@State var deviceManager: DeviceManager?
@State var localVideoStream:[LocalVideoStream]?
@State var teamsIncomingCall: TeamsIncomingCall?
@State var sendingVideo:Bool = false
@State var errorMessage:String = "Unknown"
@State var remoteVideoStreamData:[Int32:RemoteVideoStreamData] = [:]
@State var previewRenderer:VideoStreamRenderer? = nil
@State var previewView:RendererView? = nil
@State var remoteRenderer:VideoStreamRenderer? = nil
@State var remoteViews:[RendererView] = []
@State var remoteParticipant: RemoteParticipant?
@State var remoteVideoSize:String = "Unknown"
@State var isIncomingCall:Bool = false
@State var callObserver:CallObserver?
@State var remoteParticipantObserver:RemoteParticipantObserver?
var body: some View {
NavigationView {
ZStack{
Form {
Section {
TextField("Who would you like to call?", text: $callee)
Button(action: startCall) {
Text("Start Teams Call")
}.disabled(teamsCallAgent == nil)
Button(action: endCall) {
Text("End Teams Call")
}.disabled(teamsCall == nil)
Button(action: toggleLocalVideo) {
HStack {
Text(sendingVideo ? "Turn Off Video" : "Turn On Video")
}
}
}
}
// Show incoming call banner
if (isIncomingCall) {
HStack() {
VStack {
Text("Incoming call")
.padding(10)
.frame(maxWidth: .infinity, alignment: .topLeading)
}
Button(action: answerIncomingCall) {
HStack {
Text("Answer")
}
.frame(width:80)
.padding(.vertical, 10)
.background(Color(.green))
}
Button(action: declineIncomingCall) {
HStack {
Text("Decline")
}
.frame(width:80)
.padding(.vertical, 10)
.background(Color(.red))
}
}
.frame(maxWidth: .infinity, alignment: .topLeading)
.padding(10)
.background(Color.gray)
}
ZStack{
VStack{
ForEach(remoteViews, id:\.self) { renderer in
ZStack{
VStack{
RemoteVideoView(view: renderer)
.frame(width: .infinity, height: .infinity)
.background(Color(.lightGray))
}
}
Button(action: endCall) {
Text("End Call")
}.disabled(teamsCall == nil)
Button(action: toggleLocalVideo) {
HStack {
Text(sendingVideo ? "Turn Off Video" : "Turn On Video")
}
}
}
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
VStack{
if(sendingVideo)
{
VStack{
PreviewVideoStream(view: previewView!)
.frame(width: 135, height: 240)
.background(Color(.lightGray))
}
}
}.frame(maxWidth:.infinity, maxHeight:.infinity,alignment: .bottomTrailing)
}
}
.navigationBarTitle("Video Calling Quickstart")
}.onAppear{
// Authenticate the client
// Initialize the TeamsCallAgent and access Device Manager
// Ask for permissions
}
}
}
//Functions and Observers
struct PreviewVideoStream: UIViewRepresentable {
let view:RendererView
func makeUIView(context: Context) -> UIView {
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
struct RemoteVideoView: UIViewRepresentable {
let view:RendererView
func makeUIView(context: Context) -> UIView {
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Ověření klienta
K inicializaci TeamsCallAgent
instance potřebujete přístupový token uživatele, který umožňuje provádět a přijímat volání. Pokud token nemáte k dispozici, projděte si dokumentaci k přístupovým tokenům uživatele.
Jakmile máte token, přidejte do zpětného onAppear
volání následující kód .ContentView.swift
Musíte nahradit <USER ACCESS TOKEN>
platným přístupovým tokenem uživatele pro váš prostředek:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
Inicializace agenta CallAgent teams a přístup k Správce zařízení
Chcete-li vytvořit instanci z objektu TeamsCallAgent
CallClient
, použijte metodu callClient.createTeamsCallAgent
, která asynchronně vrátí TeamsCallAgent
objekt po inicializaci. DeviceManager
umožňuje vytvořit výčet místních zařízení, která se dají použít při volání k přenosu zvukových datových proudů nebo datových proudů videa. Umožňuje také požádat uživatele o oprávnění pro přístup k mikrofonu nebo fotoaparátu.
self.callClient = CallClient()
let options = TeamsCallAgentOptions()
// Enable CallKit in the SDK
options.callKitOptions = CallKitOptions(with: createCXProvideConfiguration())
self.callClient?.createTeamsCallAgent(userCredential: userCredential, options: options) { (agent, error) in
if error != nil {
print("ERROR: It was not possible to create a Teams call agent.")
return
} else {
self.teamsCallAgent = agent
print("Teams Call agent successfully created.")
self.teamsCallAgent!.delegate = teamsIncomingCallHandler
self.callClient?.getDeviceManager { (deviceManager, error) in
if (error == nil) {
print("Got device manager instance")
self.deviceManager = deviceManager
} else {
print("Failed to get device manager instance")
}
}
}
}
Požádat o oprávnění
Do zpětného onAppear
volání musíme přidat následující kód, abychom požádali o oprávnění pro zvuk a video.
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
AVCaptureDevice.requestAccess(for: .video) { (videoGranted) in
/* NO OPERATION */
}
}
}
Umístění odchozího hovoru
Metoda startCall
je nastavena jako akce, která se provádí při klepnutí na tlačítko Zahájit volání. V tomto rychlém startu jsou odchozí hovory ve výchozím nastavení jenom zvuk. Abychom mohli zahájit hovor s videem, musíme ho nastavit VideoOptions
LocalVideoStream
a předat ho startCallOptions
, abychom pro hovor nastavili počáteční možnosti.
let startTeamsCallOptions = StartTeamsCallOptions()
if sendingVideo {
if self.localVideoStream == nil {
self.localVideoStream = [LocalVideoStream]()
}
let videoOptions = VideoOptions(localVideoStreams: localVideoStream!)
startTeamsCallOptions.videoOptions = videoOptions
}
let callees: [CommunicationIdentifier] = [CommunicationUserIdentifier(self.callee)]
self.teamsCallAgent?.startCall(participants: callees, options: startTeamsCallOptions) { (call, error) in
// Handle call object if successful or an error.
}
Připojení ke schůzce Teams
Metoda join
umožňuje uživateli připojit se ke schůzce týmů.
let joinTeamsCallOptions = JoinTeamsCallOptions()
if sendingVideo
{
if self.localVideoStream == nil {
self.localVideoStream = [LocalVideoStream]()
}
let videoOptions = VideoOptions(localVideoStreams: localVideoStream!)
joinTeamsCallOptions.videoOptions = videoOptions
}
// Join the Teams meeting muted
if isMuted
{
let outgoingAudioOptions = OutgoingAudioOptions()
outgoingAudioOptions.muted = true
joinTeamsCallOptions.outgoingAudioOptions = outgoingAudioOptions
}
let teamsMeetingLinkLocator = TeamsMeetingLinkLocator(meetingLink: "https://meeting_link")
self.teamsCallAgent?.join(with: teamsMeetingLinkLocator, options: joinTeamsCallOptions) { (call, error) in
// Handle call object if successful or an error.
}
TeamsCallObserver
a RemoteParticipantObserver
slouží ke správě událostí uprostřed hovoru a vzdálených účastníků. Nastavíme pozorovatele ve setTeamsCallAndObserver
funkci.
func setTeamsCallAndObserver(call:TeamsCall, error:Error?) {
if (error == nil) {
self.teamsCall = call
self.teamsCallObserver = TeamsCallObserver(self)
self.teamsCall!.delegate = self.teamsCallObserver
// Attach a RemoteParticipant observer
self.remoteParticipantObserver = RemoteParticipantObserver(self)
} else {
print("Failed to get teams call object")
}
}
Přijetí příchozího hovoru
Příchozí hovor přijmete tak, že implementujete TeamsIncomingCallHandler
zobrazení banneru příchozího hovoru, aby bylo možné hovor přijmout nebo odmítnout. Vložte následující implementaci do TeamsIncomingCallHandler.swift
souboru .
final class TeamsIncomingCallHandler: NSObject, TeamsCallAgentDelegate, TeamsIncomingCallDelegate {
public var contentView: ContentView?
private var teamsIncomingCall: TeamsIncomingCall?
private static var instance: TeamsIncomingCallHandler?
static func getOrCreateInstance() -> TeamsIncomingCallHandler {
if let c = instance {
return c
}
instance = TeamsIncomingCallHandler()
return instance!
}
private override init() {}
func teamsCallAgent(_ teamsCallAgent: TeamsCallAgent, didReceiveIncomingCall incomingCall: TeamsIncomingCall) {
self.teamsIncomingCall = incomingCall
self.teamsIncomingCall.delegate = self
contentView?.showIncomingCallBanner(self.teamsIncomingCall!)
}
func teamsCallAgent(_ teamsCallAgent: TeamsCallAgent, didUpdateCalls args: TeamsCallsUpdatedEventArgs) {
if let removedCall = args.removedCalls.first {
contentView?.callRemoved(removedCall)
self.teamsIncomingCall = nil
}
}
}
Potřebujeme vytvořit instanci tak, že do zpětného TeamsIncomingCallHandler
onAppear
ContentView.swift
volání přidáme následující kód:
Nastavte delegáta TeamsCallAgent
na po TeamsCallAgent
úspěšném vytvoření:
self.teamsCallAgent!.delegate = incomingCallHandler
Jakmile dojde k příchozímu hovoru, zavolá funkcishowIncomingCallBanner
, TeamsIncomingCallHandler
která se má zobrazit answer
a decline
tlačítko.
func showIncomingCallBanner(_ incomingCall: TeamsIncomingCall) {
self.teamsIncomingCall = incomingCall
}
Akce připojené answer
k následujícímu kódu a decline
jsou implementovány jako následující kód. Abychom mohli hovor přijmout pomocí videa, musíme zapnout místní video a nastavit možnosti AcceptCallOptions
s localVideoStream
.
func answerIncomingCall() {
let options = AcceptTeamsCallOptions()
guard let teamsIncomingCall = self.teamsIncomingCall else {
print("No active incoming call")
return
}
guard let deviceManager = deviceManager else {
print("No device manager instance")
return
}
if self.localVideoStreams == nil {
self.localVideoStreams = [LocalVideoStream]()
}
if sendingVideo
{
guard let camera = deviceManager.cameras.first else {
// Handle failure
return
}
self.localVideoStreams?.append( LocalVideoStream(camera: camera))
let videoOptions = VideoOptions(localVideoStreams: localVideosStreams!)
options.videoOptions = videoOptions
}
teamsIncomingCall.accept(options: options) { (call, error) in
// Handle call object if successful or an error.
}
}
func declineIncomingCall() {
self.teamsIncomingCall?.reject { (error) in
// Handle if rejection was successfully or not.
}
}
Přihlášení k odběru událostí
Můžeme implementovat TeamsCallObserver
třídu pro přihlášení k odběru kolekce událostí, které mají být upozorněny při změně hodnot během volání.
public class TeamsCallObserver: NSObject, TeamsCallDelegate, TeamsIncomingCallDelegate {
private var owner: ContentView
init(_ view:ContentView) {
owner = view
}
public func teamsCall(_ teamsCall: TeamsCall, didChangeState args: PropertyChangedEventArgs) {
if(teamsCall.state == CallState.connected) {
initialCallParticipant()
}
}
// render remote video streams when remote participant changes
public func teamsCall(_ teamsCall: TeamsCall, didUpdateRemoteParticipant args: ParticipantsUpdatedEventArgs) {
for participant in args.addedParticipants {
participant.delegate = self.remoteParticipantObserver
}
}
// Handle remote video streams when the call is connected
public func initialCallParticipant() {
for participant in owner.teamsCall.remoteParticipants {
participant.delegate = self.remoteParticipantObserver
for stream in participant.videoStreams {
renderRemoteStream(stream)
}
owner.remoteParticipant = participant
}
}
}
Spuštění kódu
Aplikaci můžete sestavit a spustit v simulátoru iOS tak, že vyberete Spuštění produktu > nebo pomocí klávesové zkratky (⌘-R).
Vyčištění prostředků
Pokud chcete vyčistit a odebrat předplatné služby Communication Services, můžete odstranit prostředek nebo skupinu prostředků. Odstraněním skupiny prostředků se odstraní také všechny ostatní prostředky, které jsou k ní přidružené. Přečtěte si další informace o čištění prostředků.
Další kroky
Další informace najdete v následujících článcích:
- Podívejte se na ukázku webového volání
- Informace o možnostech volání sady SDK
- Další informace o fungování volání