QuickStart: 1:1 videogesprekken toevoegen als Teams-gebruiker aan uw toepassing
Ga aan de slag met Azure Communication Services met behulp van de Communication Services-aanroep-SDK om 1:1 spraak- en videogesprekken toe te voegen aan uw app. U leert hoe u een oproep start en beantwoordt met behulp van de Azure Communication Services Calling SDK voor JavaScript.
Voorbeeldcode
Als u verder wilt gaan naar het einde, kunt u deze quickstart downloaden als voorbeeld op GitHub.
Vereisten
- Haal een Azure-account op met een actief abonnement. Gratis een account maken
- U moet Node.js 18 hebben. U kunt het MSI-installatieprogramma gebruiken om het te installeren.
- Maak een actieve Communication Services-resource. Een Communication Services-resource maken.
- Maak een token voor gebruikerstoegang om de aanroepclient te instantiëren. Meer informatie over het maken en beheren van tokens voor gebruikerstoegang.
- Haal de Thread-id van Teams op voor oproepbewerkingen met Behulp van Graph Explorer. Lees meer over het maken van chatthread-id.
Instellen
Een nieuwe Node.js-toepassing maken
Open uw terminal of opdrachtvenster om een nieuwe map voor uw app te maken en navigeer naar de map.
mkdir calling-quickstart && cd calling-quickstart
Voer npm init -y
uit om een package.json-bestand te maken met de standaardinstellingen.
npm init -y
Het pakket installeren
Gebruik de npm install
opdracht om de Sdk voor aanroepen van Azure Communication Services voor JavaScript te installeren.
Belangrijk
In deze quickstart wordt gebruikgemaakt van de nieuwste SDK-versie voor aanroepen van Azure Communication Services.
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Stel het app-framework in
In deze snelstart wordt webpack gebruikt om de toepassingsassets te bundelen. Voer de volgende opdracht uit om de webpack
npm-pakketten webpack-cli
en webpack-dev-server
npm-pakketten te installeren en weer te geven als ontwikkelingsafhankelijkheden in uw 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
Maak een index.html
bestand in de hoofdmap van uw project. We gebruiken dit bestand om een eenvoudige indeling te configureren waarmee de gebruiker een videogesprek van 1:1 kan plaatsen.
Hier volgt de code:
<!-- 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>
Web-SDK-objectmodel aanroepen van Azure Communication Services
De volgende klassen en interfaces verwerken enkele van de belangrijkste functies van de Azure Communication Services Calling SDK:
Name | Beschrijving |
---|---|
CallClient |
Het belangrijkste toegangspunt voor de Aanroepende SDK. |
AzureCommunicationTokenCredential |
Implementeert de CommunicationTokenCredential interface, die wordt gebruikt om te instantiëren teamsCallAgent . |
TeamsCallAgent |
Wordt gebruikt om Teams-oproepen te starten en te beheren. |
DeviceManager |
Wordt gebruikt voor het beheren van mediaapparaten. |
TeamsCall |
Wordt gebruikt voor het vertegenwoordigen van een Teams-oproep |
LocalVideoStream |
Wordt gebruikt voor het maken van een lokale videostream voor een cameraapparaat op het lokale systeem. |
RemoteParticipant |
Wordt gebruikt voor het vertegenwoordigen van een externe deelnemer in het gesprek |
RemoteVideoStream |
Wordt gebruikt voor het vertegenwoordigen van een externe videostream van een externe deelnemer. |
Maak een bestand in de hoofdmap van uw project index.js
dat de toepassingslogica voor deze quickstart bevat. Voeg de volgende code toe aan index.js:
// 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();
});
De lokale servercode van het webpack toevoegen
Maak een bestand in de hoofdmap van uw project met de naam webpack.config.js om de lokale serverlogica voor deze quickstart te bevatten. Voeg de volgende code toe aan webpack.config.js:
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'
]
}),
]
};
De code uitvoeren
Gebruik de webpack-dev-server
om uw app te bouwen en uit te voeren. Voer de volgende opdracht uit om de toepassingshost te bundelen op een lokale webserver:
`npx webpack serve --config webpack.config.js`
Open uw browser en navigeer op twee tabbladen naar http://localhost:8080/. Tabbladen moeten het vergelijkbare resultaat weergeven, zoals in de volgende afbeelding:
Voer op het eerste tabblad een geldig toegangstoken voor gebruikers in. Voer op het tweede tabblad nog een ander geldig toegangstoken voor gebruikers in. Raadpleeg de documentatie voor het token voor gebruikerstoegang als u nog geen toegangstokens hebt die u kunt gebruiken. Klik op beide tabbladen op de knoppen Oproepagent initialiseren. Tabbladen moeten het vergelijkbare resultaat weergeven, zoals in de volgende afbeelding:
Voer op het eerste tabblad de azure Communication Services-gebruikersidentiteit van het tweede tabblad in en selecteer de knop Gesprek starten. Het eerste tabblad start de uitgaande aanroep naar het tweede tabblad en de knop 'Oproep accepteren' van het tweede tabblad wordt ingeschakeld:
Selecteer op het tweede tabblad de knop Gesprek accepteren. Het gesprek wordt beantwoord en verbonden. Tabbladen moeten het vergelijkbare resultaat weergeven, zoals in de volgende afbeelding:
Beide tabbladen zijn nu in een videogesprek van 1:1 geslaagd. Beide gebruikers kunnen elkaars audio horen en elkaars videostream zien.
Ga aan de slag met Azure Communication Services met behulp van de Communication Services-aanroep-SDK om 1:1 spraak- en videogesprekken toe te voegen aan uw app. U leert hoe u een oproep start en beantwoordt met behulp van de Azure Communication Services Calling SDK voor Windows.
Voorbeeldcode
Als u verder wilt gaan naar het einde, kunt u deze quickstart downloaden als voorbeeld op GitHub.
Vereisten
Voor het voltooien van deze zelfstudie moet aan de volgende vereisten worden voldaan:
- Een Azure-account met een actief abonnement. Gratis een account maken
- Installeer Visual Studio 2022 met Universeel Windows-platform ontwikkelworkload.
- Een geïmplementeerde Communication Services-resource. Een Communication Services-resource maken. U moet uw verbindingsreeks opnemen voor deze quickstart.
- Een toegangstoken voor gebruikers voor uw Azure Communication Service.
- Haal de Thread-id van Teams op voor oproepbewerkingen met Behulp van Graph Explorer. Lees meer over het maken van chatthread-id.
Instellen
Het project maken
Maak in Visual Studio een nieuw project met de sjabloon Blank App (Universal Windows) om een UWP-app (single page Universeel Windows-platform) in te stellen.
Het pakket installeren
Selecteer met de rechtermuisknop uw project en ga naar Manage Nuget Packages
1.2.0-beta.1 of superior te installeren Azure.Communication.Calling.WindowsClient
. Zorg ervoor dat Preleased opnemen is ingeschakeld.
Toegang aanvragen
Ga naar Package.appxmanifest
en selecteer Capabilities
.
Controleer Internet (Client)
en Internet (Client & Server)
om binnenkomende en uitgaande toegang tot internet te krijgen. Controleer Microphone
of u toegang hebt tot de audiofeed van de microfoon en Webcam
om toegang te krijgen tot de videofeed van de camera.
Stel het app-framework in
We moeten een eenvoudige indeling configureren om onze logica te koppelen. Om een uitgaande oproep te kunnen plaatsen, moeten TextBox
we de gebruikers-id van de aanroepende gebruiker opgeven. We hebben ook een Start/Join call
knop en een Hang up
knop nodig. A Mute
- en selectievakjes BackgroundBlur
zijn ook opgenomen in dit voorbeeld om de functies van het schakelen tussen audiostatussen en video-effecten te demonstreren.
Open het MainPage.xaml
project en voeg het knooppunt toe aan het Grid
Page
volgende:
<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>
Open de MainPage.xaml.cs
inhoud en vervang deze door de volgende implementatie:
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
}
}
Objectmodel
De volgende tabel bevat de klassen en interfaces die enkele van de belangrijkste functies van de Aanroepende SDK van Azure Communication Services verwerken:
Name | Beschrijving |
---|---|
CallClient |
Dit CallClient is het belangrijkste toegangspunt voor de Calling SDK. |
TeamsCallAgent |
Het TeamsCallAgent wordt gebruikt om oproepen te starten en te beheren. |
TeamsCommunicationCall |
Deze TeamsCommunicationCall wordt gebruikt om een doorlopend gesprek te beheren. |
CallTokenCredential |
De CallTokenCredential wordt gebruikt als de tokenreferentie om de TeamsCallAgent . |
CallIdentifier |
Het CallIdentifier wordt gebruikt om de identiteit van de gebruiker weer te geven, die een van de volgende opties kan zijn: MicrosoftTeamsUserCallIdentifier , UserCallIdentifier , enzovoort PhoneNumberCallIdentifier . |
De client verifiëren
Initialiseer een TeamsCallAgent
exemplaar met een token voor gebruikerstoegang waarmee we oproepen kunnen plaatsen en ontvangen, en eventueel een DeviceManager-exemplaar verkrijgen om een query uit te voeren op clientapparaatconfiguraties.
Vervang in de code door <AUTHENTICATION_TOKEN>
een token voor gebruikerstoegang. Raadpleeg de documentatie inzake Token voor gebruikerstoegang als u nog geen token hebt.
Voeg een functie toe InitCallAgentAndDeviceManagerAsync
, waarmee de SDK wordt opgestart. Deze helper kan worden aangepast om te voldoen aan de vereisten van uw toepassing.
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;
}
Een oproep starten
Voeg de implementatie toe aan het CallButton_Click
om verschillende soorten aanroepen te starten met het teamsCallAgent
object dat we hebben gemaakt, en koppel RemoteParticipantsUpdated
en StateChanged
gebeurtenis-handlers aan TeamsCommunicationCall
het object.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
teamsCall = await StartCteCallAsync(callString);
if (teamsCall != null)
{
teamsCall.StateChanged += OnStateChangedAsync;
}
}
Een gesprek beëindigen
Beëindig het huidige gesprek wanneer op de Hang up
knop wordt geklikt. Voeg de implementatie toe aan de HangupButton_Click om een gesprek te beëindigen en stop de preview- en 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 });
}
}
Geluid dempen/dempen opheffen
Demp de uitgaande audio wanneer op de Mute
knop wordt geklikt. Voeg de implementatie toe aan de MuteLocal_Click om de aanroep te dempen.
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
}
}
Het gesprek starten
Zodra een StartTeamsCallOptions
object is verkregen, TeamsCallAgent
kunt u de Teams-oproep initiëren:
private async Task<TeamsCommunicationCall> StartCteCallAsync(string cteCallee)
{
var options = new StartTeamsCallOptions();
var teamsCall = await this.teamsCallAgent.StartCallAsync( new MicrosoftTeamsUserCallIdentifier(cteCallee), options);
return call;
}
Een inkomende oproep accepteren
TeamsIncomingCallReceived
gebeurtenissink is ingesteld in de SDK bootstrap-helper InitCallAgentAndDeviceManagerAsync
.
this.teamsCallAgent.IncomingCallReceived += OnIncomingCallAsync;
De toepassing heeft de mogelijkheid om te configureren hoe de inkomende oproep moet worden geaccepteerd, zoals soorten video- en audiostreams.
private async void OnIncomingCallAsync(object sender, TeamsIncomingCallReceivedEventArgs args)
{
var teamsIncomingCall = args.IncomingCall;
var acceptteamsCallOptions = new AcceptTeamsCallOptions() { };
teamsCall = await teamsIncomingCall.AcceptAsync(acceptteamsCallOptions);
teamsCall.StateChanged += OnStateChangedAsync;
}
Deelnemen aan een Teams-gesprek
Gebruiker kan ook deelnemen aan een bestaande oproep door een koppeling door te geven
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
JoinTeamsCallOptions options = new JoinTeamsCallOptions();
TeamsCall call = await teamsCallAgent.JoinAsync(link, options);
Gebeurtenis voor het wijzigen van oproepstatus controleren en erop reageren
StateChanged
gebeurtenis op TeamsCommunicationCall
object wordt geactiveerd wanneer transacties worden aangeroepen van de ene status naar de andere. De toepassing biedt de mogelijkheden om de statuswijzigingen in de gebruikersinterface weer te geven of bedrijfslogica in te voegen.
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;
}
}
}
De code uitvoeren
U kunt de code bouwen en uitvoeren in Visual Studio. Voor oplossingsplatformen ondersteunen ARM64
we , x64
en x86
.
U kunt een uitgaande oproep doen door een gebruikers-id op te geven in het tekstveld en op de Start Call/Join
knop te klikken. Bellen 8:echo123
maakt verbinding met een echobot. Deze functie is ideaal om aan de slag te gaan en te controleren of uw audioapparaten werken.
Ga aan de slag met Azure Communication Services met behulp van de Communication Services-aanroep-SDK om 1:1 spraak- en videogesprekken toe te voegen aan uw app. U leert hoe u een oproep start en beantwoordt met behulp van de Azure Communication Services Calling SDK voor Java.
Voorbeeldcode
Als u verder wilt gaan naar het einde, kunt u deze quickstart downloaden als voorbeeld op GitHub.
Vereisten
- Een Azure-account met een actief abonnement. Gratis een account maken
- Android Studio, voor het maken van uw Android-toepassing.
- Een geïmplementeerde Communication Services-resource. Een Communication Services-resource maken. U moet uw verbindingsreeks opnemen voor deze quickstart.
- Een toegangstoken voor gebruikers voor uw Azure Communication Service.
- Haal de Thread-id van Teams op voor oproepbewerkingen met Behulp van Graph Explorer. Lees meer over het maken van chatthread-id.
Instellen
Een Android-app maken met een lege activiteit
Selecteer Een nieuw Android Studio-project starten in Android Studio.
Selecteer de projectsjabloon Lege activiteit onder Telefoon en tablet.
Selecteer minimum-SDK van 'API 26: Android 8.0 (Oreo)' of hoger.
Het pakket installeren
Zoek de build.gradle op projectniveau op en vergeet niet mavenCentral()
toe te voegen aan de lijst met opslagplaatsen onder buildscript
en allprojects
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Voeg vervolgens in de build.gradle op moduleniveau de volgende regels toe aan de secties Afhankelijkheden en Android
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'
...
}
Machtigingen toevoegen aan het toepassingsmanifest
Als u machtigingen wilt aanvragen die nodig zijn om een aanroep te doen, moeten ze worden gedeclareerd in het toepassingsmanifest (app/src/main/AndroidManifest.xml
). Vervang de inhoud van het bestand door de volgende 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>
De indeling van de app instellen
Er moeten twee items worden toegevoegd: een tekstvak voor de id van de persoon die wordt gebeld en een knop om te bellen. Deze invoer kan worden toegevoegd via de ontwerpfunctie of door de xml-indeling te bewerken. Maak een knop met een id van call_button
en een tekstinvoer van callee_id
. Navigeer naar (app/src/main/res/layout/activity_main.xml
) en vervang de inhoud van het bestand door de volgende 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>
De hoofdopbouw en -bindingen van de activiteit maken
Nu de indeling is gemaakt, kunnen de bindingen en de basisopzet van de activiteit worden toegevoegd. De activiteit verwerkt het aanvragen van runtimemachtigingen, het maken van de teams-oproepagent en het plaatsen van de oproep wanneer de knop wordt ingedrukt. Elk wordt behandeld in een eigen sectie. De onCreate
methode wordt overschreven om de bindingen voor de oproepknop aan te roepen getAllPermissions
en createTeamsAgent
toe te voegen. Deze gebeurtenis vindt slechts één keer plaats wanneer de activiteit wordt gemaakt. Zie de handleiding Inzicht in de levenscyclus van activiteiten voor meer informatieonCreate
.
Ga naar MainActivity.java en vervang de inhoud door de volgende 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
}
}
Machtigingen tijdens runtime aanvragen
Voor Android 6.0 en hoger (API-niveau 23) en targetSdkVersion
23 of hoger worden machtigingen toegekend tijdens de runtime in plaats van tijdens de installatie van de app. Om deze te ondersteunen, getAllPermissions
kan worden geïmplementeerd om te bellen ActivityCompat.checkSelfPermission
en ActivityCompat.requestPermissions
voor elke vereiste machtiging.
/**
* 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);
}
}
Notitie
Houd tijdens het ontwerpen van uw app rekening met het moment waarop deze machtigingen moeten worden aangevraagd. Machtigingen moeten worden aangevraagd wanneer u ze nodig hebt, niet van tevoren. Zie de Handleiding voor Android-machtigingen voor meer informatie.
Objectmodel
De volgende klassen en interfaces verwerken enkele van de belangrijkste functies van de Azure Communication Services Calling SDK:
Name | Beschrijving |
---|---|
CallClient |
Dit CallClient is het belangrijkste toegangspunt voor de Calling SDK. |
TeamsCallAgent |
Het TeamsCallAgent wordt gebruikt om oproepen te starten en te beheren. |
TeamsCall |
De TeamsCall functie die wordt gebruikt voor het vertegenwoordigen van een Teams-oproep. |
CommunicationTokenCredential |
De CommunicationTokenCredential wordt gebruikt als de tokenreferentie om de TeamsCallAgent . |
CommunicationIdentifier |
De CommunicationIdentifier wordt gebruikt als een ander type deelnemer dat deel kan uitmaken van een gesprek. |
Een agent maken op basis van het toegangstoken voor gebruikers
Met een gebruikerstoken kan een geverifieerde oproepagent worden geïnstantieerd. Over het algemeen wordt dit token gegenereerd op basis van een service met verificatie die specifiek is voor de toepassing. Raadpleeg de handleiding gebruikerstoegangstokens voor meer informatie over tokens voor gebruikerstoegang.
Voor de quickstart vervangt u <User_Access_Token>
door een gebruikerstoegangstoken dat voor uw Azure Communication Service-resource is gegenereerd.
/**
* 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();
}
}
Een aanroep starten met behulp van de aanroepagent
Het plaatsen van de oproep kan worden gedaan via de oproepagent van teams en vereist alleen een lijst met oproep-id's en de oproepopties. Voor de quickstart worden de standaardoproepopties zonder video en één nummerweergave uit de tekstinvoer gebruikt.
/**
* 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);
}
Een gesprek beantwoorden
Het accepteren van een oproep kan worden uitgevoerd met behulp van de teams-oproepagent met alleen een verwijzing naar de huidige context.
public void acceptACall(TeamsIncomingCall teamsIncomingCall){
teamsIncomingCall.accept(this);
}
Deelnemen aan een Teams-gesprek
Een gebruiker kan deelnemen aan een bestaande oproep door een koppeling door te geven.
/**
* Join a call using a teams meeting link.
*/
public TeamsCall joinTeamsCall(TeamsCallAgent teamsCallAgent){
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
TeamsCall call = teamsCallAgent.join(this, link);
}
Deelnemen aan een Teams-gesprek met opties
We kunnen ook deelnemen aan een bestaande oproep met vooraf ingestelde opties, zoals gedempt worden.
/**
* 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);
}
Inkomende oproeplistener instellen
Om binnenkomende oproepen en andere acties die niet door deze gebruiker worden uitgevoerd, te kunnen detecteren, moeten listeners worden ingesteld.
private TeamsIncomingCall teamsincomingCall;
teamsCallAgent.addOnIncomingCallListener(this::handleIncomingCall);
private void handleIncomingCall(TeamsIncomingCall incomingCall) {
this.teamsincomingCall = incomingCall;
}
De app starten en de Echo-bot aanroepen
De app kan nu worden gestart met behulp van de knop App uitvoeren op de werkbalk (Shift + F10). Controleer of u gesprekken kunt plaatsen door te bellen 8:echo123
. Een vooraf opgenomen bericht wordt afgespeeld en herhaalt het bericht vervolgens weer naar u.
Ga aan de slag met Azure Communication Services met behulp van de Communication Services-aanroep-SDK om er een toe te voegen aan één videogesprek aan uw app. U leert hoe u een videogesprek start en beantwoordt met behulp van de Azure Communication Services Calling SDK voor iOS met behulp van Teams-identiteit.
Voorbeeldcode
Als u verder wilt gaan naar het einde, kunt u deze quickstart downloaden als voorbeeld op GitHub.
Vereisten
- Haal een Azure-account op met een actief abonnement. Gratis een account maken
- Een Mac waarop Xcode wordt uitgevoerd, evenals een geldig ontwikkelaarscertificaat dat is geïnstalleerd in uw Sleutelhanger.
- Maak een actieve Communication Services-resource. Een Communication Services-resource maken. U moet uw verbindingsreeks opnemen voor deze quickstart.
- Een toegangstoken voor gebruikers voor uw Azure Communication Service.
- Haal de Thread-id van Teams op voor oproepbewerkingen met Behulp van Graph Explorer. Meer informatie over het maken van chatthread-id
Instellen
Het Xcode-project maken
Maak in Xcode een nieuw iOS-project en selecteer de sjabloon Single View-app (Toepassing met één weergave). In deze zelfstudie wordt gebruikgemaakt van het SwiftUI-framework, dus u moet de taal instellen op Swift en de gebruikersinterface op SwiftUI. Tijdens deze quickstart maakt u geen tests. U kunt Tests opnemen uitschakelen.
CocoaPods installeren
Gebruik deze handleiding om CocoaPods op uw Mac te installeren.
Installeer het pakket en de afhankelijkheden met CocoaPods
Als u een
Podfile
voor uw toepassing wilt maken, opent u de terminal en gaat u naar de projectmap en voert u pod init uit.Voeg de volgende code toe aan de
Podfile
code en sla deze op. Zie SDK-ondersteuningsversies.
platform :ios, '13.0'
use_frameworks!
target 'VideoCallingQuickstart' do
pod 'AzureCommunicationCalling', '~> 2.10.0'
end
Voer pod-installatie uit.
Open de
.xcworkspace
met Xcode.
Toegang tot de microfoon en camera aanvragen
Voor toegang tot de microfoon en camera van het apparaat moet u de lijst met gegevenseigenschappen van uw app bijwerken met een NSMicrophoneUsageDescription
en NSCameraUsageDescription
. U stelt de gekoppelde waarde in op een tekenreeks die het dialoogvenster bevat dat het systeem gebruikt om toegang van de gebruiker aan te vragen.
Klik met de rechtermuisknop op de Info.plist
vermelding van de projectstructuur en selecteer Open As > Source Code. Voeg de volgende regels toe in de bovenste sectie <dict>
en sla het bestand op.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for video calling</string>
Stel het app-framework in
Open het bestand van ContentView.swift
uw project en voeg boven aan het bestand een importdeclaratie toe om de AzureCommunicationCalling
bibliotheek te importeren en AVFoundation
. AVFoundation wordt gebruikt om audiomachtigingen van code vast te leggen.
import AzureCommunicationCalling
import AVFoundation
Objectmodel
De volgende klassen en interfaces verwerken enkele van de belangrijkste functies van de Azure Communication Services Calling SDK voor iOS.
Name | Beschrijving |
---|---|
CallClient |
Dit CallClient is het belangrijkste toegangspunt voor de Calling SDK. |
TeamsCallAgent |
Het TeamsCallAgent wordt gebruikt om oproepen te starten en te beheren. |
TeamsIncomingCall |
Deze TeamsIncomingCall wordt gebruikt om binnenkomende teams-oproepen te accepteren of af te wijzen. |
CommunicationTokenCredential |
De CommunicationTokenCredential wordt gebruikt als de tokenreferentie om de TeamsCallAgent . |
CommunicationIdentifier |
Het CommunicationIdentifier wordt gebruikt om de identiteit van de gebruiker weer te geven. Dit kan een van de volgende opties zijn: CommunicationUserIdentifier , PhoneNumberIdentifier of CallingApplication . |
De Teams-oproepagent maken
Vervang de implementatie van ContentView struct
door enkele eenvoudige UI-besturingselementen waarmee een gebruiker een oproep kan initiëren en beëindigen. In deze quickstart voegen we bedrijfslogica toe aan deze besturingselementen.
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()
}
}
De client verifiëren
Als u een TeamsCallAgent
exemplaar wilt initialiseren, hebt u een token voor gebruikerstoegang nodig waarmee het kan maken en oproepen kunnen ontvangen. Raadpleeg de documentatie voor het toegangstoken van de gebruiker als u geen token beschikbaar hebt.
Zodra u een token hebt, voegt u de volgende code toe aan de onAppear
callback in ContentView.swift
. U moet vervangen door <USER ACCESS TOKEN>
een geldig toegangstoken voor gebruikers voor uw resource:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
Initialiseer de Teams CallAgent en open Apparaatbeheer
Als u een TeamsCallAgent
exemplaar wilt maken op basis van een CallClient
, gebruikt u de callClient.createTeamsCallAgent
methode die asynchroon een TeamsCallAgent
object retourneert zodra het is geïnitialiseerd. DeviceManager
hiermee kunt u lokale apparaten opsommen die kunnen worden gebruikt in een gesprek om audio-/videostreams te verzenden. Hiermee kunt u ook toestemming vragen van een gebruiker om toegang te krijgen tot microfoon/camera.
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")
}
}
}
}
Vragen om machtigingen
We moeten de volgende code toevoegen aan de onAppear
callback om machtigingen voor audio en video te vragen.
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
AVCaptureDevice.requestAccess(for: .video) { (videoGranted) in
/* NO OPERATION */
}
}
}
Een uitgaande oproep plaatsen
De startCall
methode wordt ingesteld als de actie die wordt uitgevoerd wanneer op de knop Gesprek starten wordt getikt. In deze quickstart zijn uitgaande oproepen alleen standaard audio. Als u een gesprek met video wilt starten, moet u instellen VideoOptions
met LocalVideoStream
en doorgeven om startCallOptions
de eerste opties voor het gesprek in te stellen.
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.
}
Deelnemen aan een Teams-vergadering
Met de join
methode kan de gebruiker deelnemen aan een teamsvergadering.
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
en RemotePariticipantObserver
worden gebruikt voor het beheren van mid-call gebeurtenissen en externe deelnemers. We stellen de waarnemers in de setTeamsCallAndObserver
functie in.
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")
}
}
Een inkomende oproep beantwoorden
Als u een inkomende oproep wilt beantwoorden, implementeert u een TeamsIncomingCallHandler
om de banner voor binnenkomende oproepen weer te geven om de oproep te beantwoorden of te weigeren. Plaats de volgende implementatie in TeamsIncomingCallHandler.swift
.
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
}
}
}
We moeten een exemplaar maken van TeamsIncomingCallHandler
door de volgende code toe te voegen aan de onAppear
callback in ContentView.swift
:
Stel een gemachtigde in op de TeamsCallAgent
gedelegeerde nadat het TeamsCallAgent
is gemaakt:
self.teamsCallAgent!.delegate = incomingCallHandler
Zodra er een binnenkomende aanroep is, roept de TeamsIncomingCallHandler
functie showIncomingCallBanner
aan om weer te geven answer
en decline
te knopen.
func showIncomingCallBanner(_ incomingCall: TeamsIncomingCall) {
self.teamsIncomingCall = incomingCall
}
De acties die zijn gekoppeld aan answer
en decline
worden geïmplementeerd als de volgende code. Om het gesprek met video te beantwoorden, moeten we de lokale video inschakelen en de opties instellen.AcceptCallOptions
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.
}
}
Abonneren op gebeurtenissen
We kunnen een TeamsCallObserver
klasse implementeren om u te abonneren op een verzameling gebeurtenissen die moeten worden gewaarschuwd wanneer de waarden tijdens de aanroep veranderen.
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
}
}
}
De code uitvoeren
U kunt uw app bouwen en uitvoeren op iOS-simulator door ProductUitvoering > te selecteren of door de sneltoets (⌘-R) te gebruiken.
Resources opschonen
Als u een Communication Services-abonnement wilt opschonen en verwijderen, kunt u de resource of resourcegroep verwijderen. Als u de resourcegroep verwijdert, worden ook alle bijbehorende resources verwijderd. Meer informatie over het opschonen van resources.
Volgende stappen
Raadpleeg voor meer informatie de volgende artikelen:
- Bekijk ons voorbeeld van webgesprekken
- Meer informatie over de mogelijkheden van calling-SDK
- Meer informatie over de werking van aanroepen