Schnellstart: Hinzufügen von 1:1-Videoanrufen zu Ihrer Anwendung als Teams-Benutzer
Steigen Sie in Azure Communication Services ein, indem Sie das Calling-SDK von Communication Services nutzen, um Ihrer App 1:1-Sprach- und Videoanrufe hinzuzufügen. Hier erfahren Sie, wie Sie einen Anruf mithilfe des Calling SDK von Azure Communication Services für JavaScript beginnen und beantworten.
Beispielcode
Wenn Sie direkt zum Ende springen möchten, können Sie diese Schnellstartanleitung als Beispiel auf GitHub herunterladen.
Voraussetzungen
- Rufen Sie ein Azure-Konto mit einem aktiven Abonnement ab. Sie können kostenlos ein Konto erstellen.
- Sie benötigen Node.js 18. Sie können das MSI-Installationsprogramm für die Installation verwenden.
- Erstellen Sie eine aktive Communication Services-Ressource. Erstellen Sie eine Communication Services-Ressource.
- Erstellen Sie ein Benutzerzugriffstoken, um den Anrufclient zu instanziieren. Lernen Sie mehr über das Erstellen und Verwalten von Benutzerzugriffstoken.
- Rufen Sie die Teams-Thread-ID für Aufrufvorgänge mithilfe des Graph-Testers ab. Hier können Sie mehr über das Erstellen der Chatthread-ID erfahren.
Einrichten
Erstellen einer neuen Node.js-Anwendung
Öffnen Sie das Terminal- oder Befehlsfenster, erstellen Sie ein neues Verzeichnis für Ihre App, und navigieren Sie zu diesem Verzeichnis.
mkdir calling-quickstart && cd calling-quickstart
Führen Sie npm init -y
aus, um die Datei package.json mit den Standardeinstellungen zu erstellen.
npm init -y
Installieren des Pakets
Verwenden Sie den Befehl npm install
, um das Azure Communication Services Calling SDK für JavaScript zu installieren.
Wichtig
In dieser Schnellstartanleitung wird die aktuelle Version des Azure Communication Services Calling SDK verwendet.
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Einrichten des App-Frameworks
In dieser Schnellstartanleitung wird Webpack verwendet, um die Anwendungsressourcen zu bündeln. Führen Sie den folgenden Befehl aus, um die npm-Pakete webpack
, webpack-cli
und webpack-dev-server
zu installieren und diese als Entwicklungsabhängigkeiten in package.json
aufzulisten:
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
Erstellen Sie im Stammverzeichnis Ihres Projekts die Datei index.html
. Diese Datei wird zum Konfigurieren eines grundlegenden Layouts verwendet, das es dem Benutzer ermöglicht, einen 1:1-Videoanruf zu tätigen.
Der Code lautet wie folgt:
<!-- 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>
Objektmodell des Azure Communication Services Calling Web SDK
Die folgenden Klassen und Schnittstellen befassen sich mit einigen der wichtigsten Features des Azure Communication Services Calling SDK:
Name | BESCHREIBUNG |
---|---|
CallClient |
Der Haupteinstiegspunkt des Calling SDK. |
AzureCommunicationTokenCredential |
Implementiert die CommunicationTokenCredential -Schnittstelle zum Instanziieren von teamsCallAgent . |
TeamsCallAgent |
Dient zum Starten und Verwalten von Teams-Anrufen. |
DeviceManager |
Dient zum Verwalten von Mediengeräten. |
TeamsCall |
Dient zum Darstellen eines Teams-Anrufs. |
LocalVideoStream |
Dient zum Erstellen eines lokalen Videostreams für ein Kameragerät auf dem lokalen System. |
RemoteParticipant |
Dient zum Darstellen eines Remoteteilnehmers für den Anruf. |
RemoteVideoStream |
Dient zum Darstellen eines Remotevideostreams von einem Remoteteilnehmer. |
Erstellen Sie im Stammverzeichnis Ihres Projekts eine Datei mit dem Namen index.js
, die die Anwendungslogik für diese Schnellstartanleitung enthalten soll. Fügen Sie „index.js“ den folgenden Code hinzu:
// 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 udpates.
subscribeToCall = (call) => {
try {
// Inspect the initial call.id value.
console.log(`Call Id: ${call.id}`);
//Subsribe 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 udpates.
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 remoteParticiapant 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();
});
Hinzufügen des lokalen Webpack-Servercodes
Erstellen Sie im Stammverzeichnis Ihres Projekts eine Datei mit dem Namen webpack.config.js, die die lokale Serverlogik für diesen Schnellstart enthalten soll. Fügen Sie der Datei webpack.config.js den folgenden Code hinzu:
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'
]
}),
]
};
Ausführen des Codes
Verwenden Sie webpack-dev-server
, um Ihre App zu erstellen und auszuführen. Führen Sie den folgenden Befehl aus, um den Anwendungshost auf einem lokalen Webserver zu bündeln:
`npx webpack serve --config webpack.config.js`
Öffnen Sie Ihren Browser, und navigieren Sie auf zwei Registerkarten zu http://localhost:8080/.. Die Registerkarten sollten ein ähnliches Ergebnis wie in der folgenden Abbildung anzeigen:
Geben Sie auf der ersten Registerkarte ein gültiges Benutzerzugriffstoken ein. Geben Sie auf der zweiten Registerkarte ein anderes gültiges Benutzerzugriffstoken ein. Wenn Sie noch über kein Zugriffstoken verfügen, finden Sie in der Dokumentation zu Benutzerzugriffstoken weitere Informationen. Klicken Sie auf beiden Registerkarten auf die Schaltflächen „Initialize Call Agent“ (Anruf-Agent initialisieren). Die Registerkarten sollten ein ähnliches Ergebnis wie in der folgenden Abbildung anzeigen:
Geben Sie auf der ersten Registerkarte die Azure Communication Services-Benutzeridentität der zweiten Registerkarte ein, und klicken Sie auf die Schaltfläche „Anruf starten“. Auf der ersten Registerkarte wird der ausgehende Aufruf der zweiten Registerkarte gestartet, und die Schaltfläche „Accept Call“ (Anruf annehmen) der zweiten Registerkarte wird aktiviert:
Klicken Sie auf der zweiten Registerkarte auf die Schaltfläche „Accept Call“ (Anruf annehmen). Der Anruf wird angenommen, und eine Verbindung wird hergestellt. Die Registerkarten sollten ein ähnliches Ergebnis wie in der folgenden Abbildung anzeigen:
Auf beiden Registerkarten ist nun ein 1:1-Videoanruf erfolgreich hergestellt worden. Beide Benutzer können die Audioausgabe des jeweils anderen hören und den Videostream sehen.
Steigen Sie in Azure Communication Services ein, indem Sie das Calling-SDK von Communication Services nutzen, um Ihrer App 1:1-Sprach- und Videoanrufe hinzuzufügen. Hier erfahren Sie, wie Sie einen Anruf mithilfe des Calling SDK von Azure Communication Services für Windows beginnen und beantworten.
Beispielcode
Wenn Sie direkt zum Ende springen möchten, können Sie diese Schnellstartanleitung als Beispiel auf GitHub herunterladen.
Voraussetzungen
Zum Durchführen dieses Tutorials benötigen Sie Folgendes:
- Ein Azure-Konto mit einem aktiven Abonnement. Sie können kostenlos ein Konto erstellen.
- Installieren Sie Visual Studio 2022 mit der Workload „Entwicklung für die universelle Windows-Plattform“.
- Eine bereitgestellte Communication Services-Ressource. Erstellen Sie eine Communication Services-Ressource. Für diese Schnellstartanleitung müssen Sie Ihre Verbindungszeichenfolge erfassen.
- Ein Benutzerzugriffstoken für Ihren Azure Communication Service
- Rufen Sie die Teams-Thread-ID für Aufrufvorgänge mithilfe des Graph-Testers ab. Hier können Sie mehr über das Erstellen der Chatthread-ID erfahren.
Einrichten
Erstellen des Projekts
Erstellen Sie in Visual Studio ein neues Projekt mit der Vorlage Leere App (Universelle Windows-App) , um eine einseitige UWP-App (Universelle Windows-Plattform) einzurichten.
Installieren des Pakets
Klicken Sie mit der rechten Maustaste auf Ihr Projekt, und wechseln Sie zu Manage Nuget Packages
, um Azure.Communication.Calling.WindowsClient
1.2.0-beta.1 oder höher zu installieren. Vergewissern Sie sich, dass „Vorabversion einbeziehen“ aktiviert ist.
Anfordern des Zugriffs
Wechseln Sie zu Package.appxmanifest
, und wählen Sie Capabilities
aus.
Aktivieren Sie Internet (Client)
und Internet (Client & Server)
, um ein- und ausgehenden Zugriff auf das Internet zu erhalten. Markieren Sie Microphone
, um auf den Audiofeed des Mikrofons zuzugreifen und Webcam
, um auf den Videofeed der Kamera zuzugreifen.
Einrichten des App-Frameworks
Wir müssen ein einfaches Layout konfigurieren, um unsere Logik anzufügen. Um einen ausgehenden Anruf zu tätigen, benötigen wir ein TextBox
-Element, um die Benutzer-ID des Angerufenen bereitzustellen. Außerdem benötigen wir die Schaltflächen Start/Join call
und Hang up
. In diesem Beispiel sind auch die Kontrollkästchen A Mute
und a BackgroundBlur
enthalten, um die Funktionen des Umschaltens von Audiozuständen und Videoeffekten zu veranschaulichen.
Öffnen Sie das MainPage.xaml
-Element Ihres Projekts, und fügen Sie Ihrer Page
den Knoten Grid
hinzu:
<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>
Öffnen Sie MainPage.xaml.cs
, und ersetzen Sie den Inhalt durch folgende Implementierung:
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
}
}
Objektmodell
Die folgende Tabelle führt die Klassen und Schnittstellen auf, die sich mit einigen der wichtigsten Features des Calling SDK von Azure Communication Services befassen:
Name | BESCHREIBUNG |
---|---|
CallClient |
CallClient ist der Haupteinstiegspunkt des Calling SDK. |
TeamsCallAgent |
TeamsCallAgent dient zum Starten und Verwalten von Anrufen. |
TeamsCommunicationCall |
TeamsCommunicationCall wird verwendet, um einen laufenden Anruf zu verwalten. |
CallTokenCredential |
CallTokenCredential dient als tokengestützte Anmeldeinformation zum Instanziieren von TeamsCallAgent . |
CallIdentifier |
CallIdentifier wird zur Darstellung der Identität des Benutzers verwendet, die eine der folgenden Optionen sein kann: MicrosoftTeamsUserCallIdentifier , UserCallIdentifier , PhoneNumberCallIdentifier usw. |
Authentifizieren des Clients
Initialisieren Sie eine TeamsCallAgent
-Instanz mit einem Benutzerzugriffs-Token, mit dem wir Anrufe tätigen und empfangen können, und rufen Sie optional eine DeviceManager-Instanz ab, um auf Client-Gerätekonfigurationen abzufragen.
Ersetzen Sie im folgenden Code <AUTHENTICATION_TOKEN>
durch ein Benutzerzugriffs-Token. Wenn Sie noch über kein Token verfügen, finden Sie in der Dokumentation zu Benutzerzugriffstoken weitere Informationen.
Fügen Sie die Funktion InitCallAgentAndDeviceManagerAsync
hinzu , die das SDK lädt. Dieses Hilfsprogramm kann an die Anforderungen Ihrer Anwendung angepasst werden.
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;
}
Beginnen eines Anrufs
Fügen Sie die Implementierung zum CallButton_Click
hinzu, um verschiedene Arten von Aufrufen mit dem von uns erstellten Objekt teamsCallAgent
zu starten, und binden Sie die Ereignishandler RemoteParticipantsUpdated
und StateChanged
für das Objekt TeamsCommunicationCall
ein.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
teamsCall = await StartCteCallAsync(callString);
if (teamsCall != null)
{
teamsCall.StateChanged += OnStateChangedAsync;
}
}
Beenden eines Anrufs
Beenden Sie den aktuellen Anruf, nachdem auf die Schaltfläche Hang up
geklickt wurde. Fügen Sie die Implementierung zum „HangupButton_Click“ hinzu, um einen Anruf zu beenden, und beenden Sie die Vorschau und Videostreams.
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 });
}
}
Stummschalten/Lautschalten bei Audio
Schalten Sie das ausgehende Audio stumm, wenn auf die Schaltfläche Mute
geklickt wird. Fügen Sie die Implementierung zum „MuteLocal_Click“ hinzu, um den Aufruf stummzuschalten.
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
}
}
Starten des Anrufs
Sobald ein Objekt StartTeamsCallOptions
abgerufen wurde, kann TeamsCallAgent
zum Initiieren des Teams-Anrufs verwendet werden:
private async Task<TeamsCommunicationCall> StartCteCallAsync(string cteCallee)
{
var options = new StartTeamsCallOptions();
var teamsCall = await this.teamsCallAgent.StartCallAsync( new MicrosoftTeamsUserCallIdentifier(cteCallee), options);
return call;
}
Annehmen eines eingehenden Anrufs
Die Ereignissenke TeamsIncomingCallReceived
ist im SDK-Bootstrap-Hilfsprogramm InitCallAgentAndDeviceManagerAsync
eingerichtet.
this.teamsCallAgent.IncomingCallReceived += OnIncomingCallAsync;
Die Anwendung hat die Möglichkeit, zu konfigurieren, wie der eingehende Anruf angenommen werden soll, z. B. Video- und Audiostreamtypen.
private async void OnIncomingCallAsync(object sender, TeamsIncomingCallReceivedEventArgs args)
{
var teamsIncomingCall = args.IncomingCall;
var acceptteamsCallOptions = new AcceptTeamsCallOptions() { };
teamsCall = await teamsIncomingCall.AcceptAsync(acceptteamsCallOptions);
teamsCall.StateChanged += OnStateChangedAsync;
}
Einem Teams-Anruf beitreten
Ein Benutzer kann auch einem vorhandenen Anruf beitreten, indem er einen Link übergibt
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
JoinTeamsCallOptions options = new JoinTeamsCallOptions();
TeamsCall call = await teamsCallAgent.JoinAsync(link, options);
Überwachen und Reagieren auf ein Anrufstatusänderungsereignis
Das Ereignis StateChanged
für das Objekt TeamsCommunicationCall
wird ausgelöst, wenn ein gerade ausgeführter Vorgang Transaktionen von einem Zustand in einen anderen aufruft. Der Anwendung wird die Möglichkeit geboten, die Zustandsänderungen auf der Benutzeroberfläche widerzuspiegeln oder Geschäftslogiken einzufügen.
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;
}
}
}
Ausführen des Codes
Sie können den Build in Visual Studio erstellen und den Code ausführen. Für Lösungsplattformen unterstützen wir ARM64
, x64
und x86
.
Sie können einen ausgehenden Anruf tätigen, indem Sie eine Benutzer-ID im Textfeld eingeben und auf die Schaltfläche Start Call/Join
klicken. Wenn Sie 8:echo123
anrufen, werden Sie mit einem Echo-Bot verbunden. Dieses Feature eignet sich hervorragend für die ersten Schritte und zum Überprüfen, ob Ihre Audiogeräte funktionieren.
Steigen Sie in Azure Communication Services ein, indem Sie das Calling-SDK von Communication Services nutzen, um Ihrer App 1:1-Sprach- und Videoanrufe hinzuzufügen. Hier erfahren Sie, wie Sie einen Anruf mithilfe des Calling SDK von Azure Communication Services für Java beginnen und beantworten.
Beispielcode
Wenn Sie direkt zum Ende springen möchten, können Sie diese Schnellstartanleitung als Beispiel auf GitHub herunterladen.
Voraussetzungen
- Ein Azure-Konto mit einem aktiven Abonnement. Sie können kostenlos ein Konto erstellen.
- Android Studio zum Erstellen Ihrer Android-Anwendung
- Eine bereitgestellte Communication Services-Ressource. Erstellen Sie eine Communication Services-Ressource. Für diese Schnellstartanleitung müssen Sie Ihre Verbindungszeichenfolge erfassen.
- Ein Benutzerzugriffstoken für Ihren Azure Communication Service
- Rufen Sie die Teams-Thread-ID für Aufrufvorgänge mithilfe des Graph-Testers ab. Hier können Sie mehr über das Erstellen der Chatthread-ID erfahren.
Einrichten
Erstellen Sie eine Android-App mit einer leeren Aktivität.
Wählen Sie in Android Studio „Start a new Android Studio project“ (Neues Android Studio-Projekt starten) aus.
Wählen Sie unter „Phone and Tablet“ (Telefon und Tablet) die Projektvorlage „Empty Activity“ (Leere Aktivität) aus.
Wählen Sie das SDK „API 26: Android 8.0 (Oreo)“ oder höher aus.
Installieren des Pakets
Suchen Sie die Datei „build.gradle“ auf Projektebene, und fügen Sie mavenCentral()
zur Liste der Repositorys unter buildscript
und allprojects
hinzu.
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Fügen Sie anschließend in der Datei „build.gradle“ auf Modulebene die folgenden Zeilen zu den Abschnitten „dependencies“ und „android“ hinzu.
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'
...
}
Hinzufügen von Berechtigungen zum Anwendungsmanifest
Damit Sie Berechtigungen anfordern können, die für einen Anruf erforderlich sind, müssen diese zunächst im Anwendungsmanifest (app/src/main/AndroidManifest.xml
) deklariert werden. Ersetzen Sie den Inhalt der Datei durch den folgenden Code:
<?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>
Festlegen des Layouts für die App
Zwei Eingaben sind erforderlich: eine Texteingabe für die Angerufenen-ID und eine Schaltfläche zum Tätigen des Anrufs. Diese Eingaben können über den Designer oder durch Bearbeiten der Layout-XML-Datei hinzugefügt werden. Erstellen Sie eine Schaltfläche mit der ID von call_button
und einer Texteingabe für callee_id
. Navigieren Sie zu app/src/main/res/layout/activity_main.xml
, und ersetzen Sie den Inhalt der Datei durch den folgenden Code:
<?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>
Erstellen des Hauptaktivitätsgerüsts und der Bindungen
Nachdem das Layout erstellt wurde, können die Bindungen und das Grundgerüst der Aktivität hinzugefügt werden. Die Aktivität verarbeitet das Anfordern von Runtime-Berechtigungen, erstellt den Teams-Anruf-Agent und platziert den Anruf, wenn die Schaltfläche gedrückt wird. Jede dieser Optionen wird in einem eigenen Abschnitt behandelt. Die onCreate
-Methode wird überschrieben, um getAllPermissions
und createTeamsAgent
aufzurufen und die Bindungen für die Schaltfläche „Anruf“ hinzuzufügen. Dieses Ereignis tritt nur einmal auf, wenn die Aktivität erstellt wird. Weitere Informationen zu onCreate
finden Sie im Leitfaden mit grundlegenden Informationen zum Aktivitätslebenszyklus.
Navigieren Sie zu MainActivity.java, und ersetzen Sie den Inhalt durch den folgenden Code:
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
}
}
Anfordern von Berechtigungen zur Runtime
Für Android 6.0 und höher (API-Ebene 23) und targetSdkVersion
23 oder höher werden Berechtigungen zur Runtime gewährt und nicht bei der Installation der App. Damit dies unterstützt wird, kann getAllPermissions
implementiert werden, um für jede erforderliche Berechtigung ActivityCompat.checkSelfPermission
und ActivityCompat.requestPermissions
aufzurufen.
/**
* 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);
}
}
Hinweis
Berücksichtigen Sie beim Entwerfen der App, wann diese Berechtigungen angefordert werden sollen. Berechtigungen müssen angefordert werden, wenn sie benötigt werden, und nicht vorher. Weitere Informationen finden Sie im Leitfaden zu Android-Berechtigungen.
Objektmodell
Die folgenden Klassen und Schnittstellen befassen sich mit einigen der wichtigsten Features des Azure Communication Services Calling SDK:
Name | BESCHREIBUNG |
---|---|
CallClient |
CallClient ist der Haupteinstiegspunkt des Calling SDK. |
TeamsCallAgent |
TeamsCallAgent dient zum Starten und Verwalten von Anrufen. |
TeamsCall |
TeamsCall dient zum Darstellen eines Teams-Anrufs. |
CommunicationTokenCredential |
CommunicationTokenCredential dient als tokengestützte Anmeldeinformation zum Instanziieren von TeamsCallAgent . |
CommunicationIdentifier |
CommunicationIdentifier wird als anderer Typ von Teilnehmer verwendet, der Teil eines Anrufs sein könnte. |
Erstellen eines Agents aus dem Benutzerzugriffstoken
Mit einem Benutzertoken kann ein authentifizierter Anruf-Agent instanziiert werden. In der Regel wird dieses Token von einem Dienst mit einer für die Anwendung spezifischen Authentifizierung generiert. Weitere Informationen zu Benutzerzugriffstoken finden Sie im Leitfaden zu Benutzerzugriffstoken.
Ersetzen Sie für den Schnellstart <User_Access_Token>
durch ein Benutzerzugriffstoken, das für Ihre Azure Communication Service-Ressource generiert wurde.
/**
* 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();
}
}
Beginnen eines Anrufs mithilfe eines Anruf-Agents
Ein Anruf kann über den Teams-Anruf-Agent getätigt werden. Dafür müssen lediglich eine Liste von Angerufenen-IDs und die Anrufoptionen bereitgestellt werden. In der Schnellstartanleitung werden die standardmäßigen Anrufoptionen ohne Video und eine einzelne Angerufenen-ID aus der Texteingabe verwendet.
/**
* 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);
}
Annehmen eines Anrufs
Das Annehmen eines Anrufs kann mithilfe des Teams-Anruf-Agents nur mithilfe eines Verweises auf den aktuellen Kontext erfolgen.
public void acceptACall(TeamsIncomingCall teamsIncomingCall){
teamsIncomingCall.accept(this);
}
Einem Teams-Anruf beitreten
Ein Benutzer kann einem vorhandenen Anruf beitreten, indem er einen Link übergibt.
/**
* Join a call using a teams meeting link.
*/
public TeamsCall joinTeamsCall(TeamsCallAgent teamsCallAgent){
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
TeamsCall call = teamsCallAgent.join(this, link);
}
Teilnehmen an einem Teams-Anruf mit Optionen
Wir können auch einem vorhandenen Anruf mit vordefinierten Optionen beitreten, z. B. stummgeschaltet werden.
/**
* 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);
}
Einrichten des Listeners für eingehende Anrufe
Um eingehende Anrufe und andere Aktionen erkennen zu können, die von diesem Benutzer nicht ausgeführt werden, müssen Listener eingerichtet sein.
private TeamsIncomingCall teamsincomingCall;
teamsCallAgent.addOnIncomingCallListener(this::handleIncomingCall);
private void handleIncomingCall(TeamsIncomingCall incomingCall) {
this.teamsincomingCall = incomingCall;
}
Starten der App und Anrufen des Echobots
Die App kann jetzt mithilfe der Schaltfläche „Run app“ (App ausführen) auf der Symbolleiste gestartet werden (UMSCHALT + F10). Überprüfen Sie, ob Sie Anrufe tätigen können, indem Sie 8:echo123
anrufen. Eine vorab aufgezeichnete Nachricht wird wiedergegeben, anschließend wird Ihre Nachricht an Sie wiederholt.
Steigen Sie in Azure Communication Services ein, indem Sie das Communication Services-SDK „Calling“ nutzen, um Ihrer App Eins-zu-Eins-Sprach- und Videoanrufe hinzuzufügen. Hier erfahren Sie, wie Sie einen Videoanruf mithilfe des Calling SDK von Azure Communication Services für iOS mithilfe der Teams-Identität starten und beantworten.
Beispielcode
Wenn Sie direkt zum Ende springen möchten, können Sie diese Schnellstartanleitung als Beispiel auf GitHub herunterladen.
Voraussetzungen
- Rufen Sie ein Azure-Konto mit einem aktiven Abonnement ab. Sie können kostenlos ein Konto erstellen.
- Einen Mac mit Xcode zusammen mit einem gültigen in Ihrer Keychain installierten Entwicklerzertifikat.
- Erstellen Sie eine aktive Communication Services-Ressource. Erstellen Sie eine Communication Services-Ressource. Für diese Schnellstartanleitung müssen Sie Ihre Verbindungszeichenfolge erfassen.
- Ein Benutzerzugriffstoken für Ihren Azure Communication Service
- Rufen Sie die Teams-Thread-ID für Aufrufvorgänge mithilfe des Graph-Testers ab. Hier können Sie mehr über das Erstellen der Chatthread-ID erfahren
Einrichten
Erstellen des Xcode-Projekts
Erstellen Sie in Xcode ein neues iOS-Projekt, und wählen Sie die Vorlage Single View App (Einzelansicht-App) aus. In diesem Tutorial wird das SwiftUI-Framework verwendet – legen Sie daher „Sprache“ auf „Swift“ und „Benutzeroberfläche“ auf „SwiftUI“ fest. Im Rahmen dieses Schnellstarts werden keine Tests erstellt. Sie können die Option Tests einschließen daher deaktivieren.
Installieren von CocoaPods
Verwenden Sie diese Anleitung, um CocoaPods auf Ihrem Mac zu installieren.
Installieren des Pakets und der Abhängigkeiten mit CocoaPods
Zum Erstellen einer
Podfile
-Datei für Ihre Anwendung öffnen Sie das Terminal, navigieren Sie zum Projektordner, und führen Sie „pod init“ aus.Fügen Sie der
Podfile
-Datei den folgenden Code hinzu und speichern Sie diese. Weitere Informationen zu unterstützten SDK-Versionen.
platform :ios, '13.0'
use_frameworks!
target 'VideoCallingQuickstart' do
pod 'AzureCommunicationCalling', '~> 2.10.0'
end
Führen Sie „pod install“ aus.
Öffnen Sie
.xcworkspace
mit Xcode.
Anfordern des Zugriffs auf Mikrofon und Kamera
Um auf Mikrofon und Kamera des Geräts zuzugreifen, müssen Sie die Liste der Informationseigenschaften Ihrer App mit NSMicrophoneUsageDescription
und NSCameraUsageDescription
aktualisieren. Legen Sie den zugehörigen Wert auf eine Zeichenfolge fest, die den Dialog beinhaltet, mit dem das System den Zugriff beim Benutzer anfordert.
Klicken Sie mit der rechten Maustaste auf den Eintrag Info.plist
der Projektstruktur, und wählen Sie anschließend „Öffnen als ...“ > „Quellcode“ aus. Fügen Sie die folgenden Zeilen im Abschnitt <dict>
der obersten Ebene hinzu, und speichern anschließend Sie die Datei.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for video calling</string>
Einrichten des App-Frameworks
Öffnen Sie die Datei ContentView.swift
Ihres Projekts und fügen Sie am Anfang der Datei eine Deklaration vom Typ „Importieren“ hinzu, um die Bibliothek AzureCommunicationCalling
und AVFoundation
zu importieren. AVFoundation wird verwendet, um Audioberechtigungen aus Code zu erfassen.
import AzureCommunicationCalling
import AVFoundation
Objektmodell
Die folgenden Klassen und Schnittstellen befassen sich mit einigen der wichtigsten Features des Azure Communication Services Calling SDK für iOS.
Name | BESCHREIBUNG |
---|---|
CallClient |
CallClient ist der Haupteinstiegspunkt des Calling SDK. |
TeamsCallAgent |
TeamsCallAgent dient zum Starten und Verwalten von Anrufen. |
TeamsIncomingCall |
TeamsIncomingCall wird verwendet, um eingehende Teams-Anrufe anzunehmen oder abzulehnen. |
CommunicationTokenCredential |
CommunicationTokenCredential dient als tokengestützte Anmeldeinformation zum Instanziieren von TeamsCallAgent . |
CommunicationIdentifier |
CommunicationIdentifier wird zur Darstellung der Identität des Benutzers verwendet, der eine der folgenden Optionen sein kann: CommunicationUserIdentifier , PhoneNumberIdentifier oder CallingApplication . |
Erstellen des Teams-Anruf-Agents
Ersetzen Sie die Implementierung der ContentView-Struktur struct
durch einige einfache Benutzeroberflächen-Steuerelemente, die einem Benutzer das Initiieren und Beenden eines Anrufs ermöglichen. In dieser Schnellstartanleitung fügen wir Geschäftslogik an diese Steuerelemente an.
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()
}
}
Authentifizieren des Clients
Um eine TeamsCallAgent
-Instanz zu initialisieren, benötigen Sie ein Benutzerzugriffs-Token, mit dem Anrufe getätigt und empfangen werden können. Wenn Sie noch nicht über ein Token verfügen, finden Sie in der Dokumentation zu Benutzerzugriffs-Token weitere Informationen.
Sobald Sie über ein Token verfügen, fügen Sie den folgenden Code zum onAppear
-Rückruf in ContentView.swift
hinzu: Sie müssen <USER ACCESS TOKEN>
durch ein gültiges Zugriffstoken-Token für Ihre Ressource ersetzen:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
Initialisieren des Teams-CallAgent und Zugreifen auf Geräte-Manager
Um eine TeamsCallAgent
-Instanz anhand von CallClient
zu erzeugen, nutzen Sie die callClient.createTeamsCallAgent
-Methode, die ein TeamsCallAgent
-Objekt asynchron zurückgibt, nachdem es initialisiert wurde. DeviceManager
ermöglicht Ihnen das Aufzählen lokaler Geräte, die in einem Anruf zur Übertragung von Audio-/Videostreams verwendet werden können. Mithilfe des Geräte-Managers können Sie auch beim Benutzer die Berechtigung für den Zugriff auf Mikrofon/Kamera anfordern.
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")
}
}
}
}
Anfordern von Berechtigungen
Wir müssen dem onAppear
Rückruf den folgenden Code hinzufügen, um Berechtigungen für Audio und Video anzufordern.
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
AVCaptureDevice.requestAccess(for: .video) { (videoGranted) in
/* NO OPERATION */
}
}
}
Tätigen eines ausgehenden Anrufs
Die Methode startCall
wird als die Aktion festgelegt, die ausgeführt wird, wenn auf die Schaltfläche „Start Call“ (Anruf starten) getippt wird. In dieser Schnellstartanleitung sind ausgehende Aufrufe standardmäßig nur Audiodaten. Um einen Anruf per Video zu starten, müssen wir VideoOptions
mit LocalVideoStream
festlegen und an startCallOptions
übergeben, um Anfangsoptionen für den Anruf festzulegen.
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.
}
An einer Teams-Besprechung teilnehmen
Mit der join
-Methode kann der Benutzer an einer Teams-Besprechung teilnehmen.
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
und RemotePariticipantObserver
werden verwendet, um Mid-Call-Ereignisse und Remote-Teilnehmer zu verwalten. Wir legen die Beobachter in der Funktion setTeamsCallAndObserver
fest.
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")
}
}
Annehmen eines eingehenden Anrufs
Implementieren Sie zum Beantworten eines eingehenden Anrufs ein TeamsIncomingCallHandler
, um das Banner für eingehende Anrufe anzuzeigen, um den Anruf zu beantworten oder zu ablehnen. Legen Sie die folgende Implementierung in TeamsIncomingCallHandler.swift
ab.
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, didRecieveIncomingCall 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
}
}
}
Wir müssen eine Instanz von TeamsIncomingCallHandler
erstellen, indem wir den folgenden Code zum onAppear
-Rückruf in ContentView.swift
hinzufügen:
Legen Sie einen Delegaten auf TeamsCallAgent
fest, nachdem TeamsCallAgent
erfolgreich erstellt wurde:
self.teamsCallAgent!.delegate = incomingCallHandler
Sobald es einen eingehenden Anruf gibt, ruft TeamsIncomingCallHandler
die Funktion showIncomingCallBanner
zum Anzeigen der Schaltfläche answer
und decline
auf.
func showIncomingCallBanner(_ incomingCall: TeamsIncomingCall) {
self.teamsIncomingCall = incomingCall
}
Die Aktionen, die an answer
und decline
angefügt sind, werden als der folgende Code implementiert. Zum Beantworten des Anrufs mit Video müssen wir das lokale Video aktivieren und die Optionen von AcceptCallOptions
mit localVideoStream
festlegen.
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.
}
}
Abonnieren von Ereignissen
Wir können eine TeamsCallObserver
Klasse implementieren, um eine Sammlung von Ereignissen zu abonnieren, über die wir benachrichtigt werden, wenn sich Werte während des Anrufs ändern.
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
}
}
}
Ausführen des Codes
Sie können Ihre App auf dem iOS-Simulator erstellen und ausführen, indem Sie „Produkt“ > „Ausführen“ auswählen oder die Tastenkombination „⌘-R“ verwenden.
Bereinigen von Ressourcen
Wenn Sie ein Communication Services-Abonnement bereinigen und entfernen möchten, können Sie die Ressource oder die Ressourcengruppe löschen. Wenn Sie die Ressourcengruppe löschen, werden auch alle anderen Ressourcen gelöscht, die ihr zugeordnet sind. Weitere Informationen zum Bereinigen von Ressourcen finden Sie hier.
Nächste Schritte
Weitere Informationen finden Sie in den folgenden Artikeln:
- Sehen Sie sich das Beispiel für Webanrufe an.
- Informieren Sie sich über die Funktionen des Calling SDK.
- Informieren Sie sich über die Funktionsweise von Anrufen.