Snabbstart: Lägga till 1:1-videosamtal som Teams-användare i ditt program
Kom igång med Azure Communication Services med hjälp av Kommunikationstjänster som anropar SDK för att lägga till röst- och videosamtal 1:1 i din app. Du får lära dig hur du startar och svarar på ett samtal med hjälp av Azure Communication Services Calling SDK för JavaScript.
Exempelkod
Om du vill gå vidare till slutet kan du ladda ned den här snabbstarten som ett exempel på GitHub.
Förutsättningar
- Skaffa ett Azure-konto med en aktiv prenumeration. Skapa ett konto utan kostnad.
- Du måste ha Node.js 18. Du kan använda installationsprogrammet msi för att installera det.
- Skapa en aktiv Communication Services-resurs. Skapa en Communication Services-resurs.
- Skapa en användaråtkomsttoken för att instansiera anropsklienten. Lär dig hur du skapar och hanterar användaråtkomsttoken.
- Hämta Teams tråd-ID till för anropsåtgärder med Hjälp av Graph Explorer. Läs mer om hur du skapar chatttråds-ID.
Konfigurera
Skapa ett nytt Node.js-program
Öppna terminalen eller kommandofönstret skapa en ny katalog för din app och navigera till katalogen.
mkdir calling-quickstart && cd calling-quickstart
Kör npm init -y
för att skapa en package.json fil med standardinställningar.
npm init -y
Installera -paketet
npm install
Använd kommandot för att installera Azure Communication Services Calling SDK för JavaScript.
Viktigt!
Den här snabbstarten använder den senaste versionen av Azure Communication Services Calling SDK.
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Konfigurera appramverket
Den här snabbstarten använder webpack för att paketera programtillgångarna. Kör följande kommando för att installera , webpack-cli
och webpack-dev-server
npm-paketen webpack
och lista dem som utvecklingsberoenden i :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
Skapa en index.html
fil i rotkatalogen för projektet. Vi använder den här filen för att konfigurera en grundläggande layout som gör att användaren kan ringa ett 1:1-videosamtal.
Här är koden:
<!-- 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>
Azure Communication Services Anropar web-SDK-objektmodell
Följande klasser och gränssnitt hanterar några av huvudfunktionerna i Azure Communication Services Calling SDK:
Name | beskrivning |
---|---|
CallClient |
Huvudinmatningspunkten till anropande SDK. |
AzureCommunicationTokenCredential |
Implementerar CommunicationTokenCredential gränssnittet, som används för att instansiera teamsCallAgent . |
TeamsCallAgent |
Används för att starta och hantera Teams-anrop. |
DeviceManager |
Används för att hantera medieenheter. |
TeamsCall |
Används för att representera ett Teams-samtal |
LocalVideoStream |
Används för att skapa en lokal videoström för en kameraenhet i det lokala systemet. |
RemoteParticipant |
Används för att representera en fjärrdeltagare i samtalet |
RemoteVideoStream |
Används för att representera en fjärrvideoström från en fjärrdeltagare. |
Skapa en fil i rotkatalogen för projektet som anropas index.js
för att innehålla programlogik för den här snabbstarten. Lägg till följande kod i 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();
});
Lägg till den lokala serverkoden för webpack
Skapa en fil i rotkatalogen i projektet med namnet webpack.config.js för att innehålla den lokala serverlogik som finns i den här snabbstarten. Lägg till följande kod i 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'
]
}),
]
};
Kör koden
Använd för webpack-dev-server
att skapa och köra din app. Kör följande kommando för att paketa programvärden i en lokal webbserver:
`npx webpack serve --config webpack.config.js`
Öppna webbläsaren och gå till två flikar http://localhost:8080/. Flikar bör visa liknande resultat som i följande bild:
På den första fliken anger du en giltig användaråtkomsttoken. På den andra fliken anger du en annan giltig användaråtkomsttoken. Se dokumentationen för användaråtkomsttoken om du inte redan har åtkomsttoken att använda. På båda flikarna klickar du på knapparna "Initiera samtalsagent". Flikar bör visa liknande resultat som i följande bild:
På den första fliken anger du användaridentiteten för Azure Communication Services på den andra fliken och väljer knappen "Starta samtal". Den första fliken startar det utgående samtalet till den andra fliken och den andra flikens "Acceptera samtal"-knappen aktiveras:
På den andra fliken väljer du knappen "Acceptera samtal". Samtalet besvaras och ansluts. Flikar bör visa liknande resultat som i följande bild:
Båda flikarna är nu i ett 1:1-videosamtal. Båda användarna kan höra varandras ljud och se varandras videoström.
Kom igång med Azure Communication Services med hjälp av Kommunikationstjänster som anropar SDK för att lägga till röst- och videosamtal 1:1 i din app. Du lär dig hur du startar och svarar på ett samtal med hjälp av Azure Communication Services Calling SDK för Windows.
Exempelkod
Om du vill gå vidare till slutet kan du ladda ned den här snabbstarten som ett exempel på GitHub.
Förutsättningar
För att slutföra den här självstudien, finns följande förhandskrav:
- Ett Azure-konto med en aktiv prenumeration. Skapa ett konto utan kostnad.
- Installera Visual Studio 2022 med arbetsbelastningen Universell Windows Platform-utveckling.
- En distribuerad Communication Services-resurs. Skapa en Communication Services-resurs. Du måste registrera niska veze för den här snabbstarten.
- En användaråtkomsttoken för azure-kommunikationstjänsten.
- Hämta Teams tråd-ID till för anropsåtgärder med Hjälp av Graph Explorer. Läs mer om hur du skapar chatttråds-ID.
Konfigurera
Skapa projektet
I Visual Studio skapar du ett nytt projekt med mallen Tom app (Universell Windows) för att konfigurera en enkelsidig UWP-app (Universal Windows Platform).
Installera -paketet
Välj projektet och gå till för att Manage Nuget Packages
installera Azure.Communication.Calling.WindowsClient
1.2.0-beta.1 eller superior. Kontrollera att Inkludera förhyrd är markerat.
Begär åtkomst
Gå till Package.appxmanifest
och välj Capabilities
.
Kontrollera Internet (Client)
och Internet (Client & Server)
för att få inkommande och utgående åtkomst till Internet. Kontrollera Microphone
om du vill komma åt mikrofonens ljudflöde och Webcam
få åtkomst till kamerans videoflöde.
Konfigurera appramverket
Vi måste konfigurera en grundläggande layout för att koppla vår logik. För att kunna ringa ett utgående samtal behöver vi ett TextBox
för att ange användar-ID:t för den anropade. Vi behöver också en Start/Join call
knapp och en Hang up
knapp. En Mute
och en BackgroundBlur
kryssrutor ingår också i det här exemplet för att demonstrera funktionerna i att växla ljudtillstånd och videoeffekter.
MainPage.xaml
Öppna projektet och lägg till noden i Grid
:Page
<Page
x:Class="CallingQuickstart.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CallingQuickstart"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Width="800" Height="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="16*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="200*"/>
<RowDefinition Height="60*"/>
<RowDefinition Height="16*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="10,10,10,10" />
<Grid x:Name="AppTitleBar" Background="LightSeaGreen">
<TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="7,7,0,0"/>
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<MediaPlayerElement x:Name="LocalVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="0" VerticalAlignment="Center" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="1" VerticalAlignment="Center" AutoPlay="True" />
</Grid>
<StackPanel Grid.Row="3" Orientation="Vertical" Grid.RowSpan="2">
<StackPanel Orientation="Horizontal">
<Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
<Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
<CheckBox x:Name="MuteLocal" Content="Mute" Margin="10,0,0,0" Click="MuteLocal_Click" Width="74"/>
</StackPanel>
</StackPanel>
<TextBox Grid.Row="5" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
</Grid>
</Page>
MainPage.xaml.cs
Öppna och ersätt innehållet med följande implementering:
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
I nästa tabell visas klasserna och gränssnitten som hanterar några av de viktigaste funktionerna i Azure Communication Services Calling SDK:
Name | beskrivning |
---|---|
CallClient |
CallClient är den viktigaste startpunkten för anropande SDK. |
TeamsCallAgent |
TeamsCallAgent Används för att starta och hantera anrop. |
TeamsCommunicationCall |
TeamsCommunicationCall Används för att hantera ett pågående anrop. |
CallTokenCredential |
CallTokenCredential Används som tokenautentiseringsuppgifter för att instansiera TeamsCallAgent . |
CallIdentifier |
CallIdentifier Används för att representera användarens identitet, vilket kan vara något av följande alternativ: MicrosoftTeamsUserCallIdentifier , UserCallIdentifier osvPhoneNumberCallIdentifier . |
Autentisera klienten
Initiera en TeamsCallAgent
instans med en användaråtkomsttoken som gör att vi kan göra och ta emot anrop och eventuellt hämta en DeviceManager-instans för att fråga efter klientenhetskonfigurationer.
I koden ersätter du <AUTHENTICATION_TOKEN>
med en användaråtkomsttoken. Se dokumentationen för användaråtkomsttoken om du inte redan har en tillgänglig token.
Lägg till InitCallAgentAndDeviceManagerAsync
funktion, som startar SDK:t. Den här hjälpen kan anpassas för att uppfylla kraven för ditt program.
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;
}
Starta ett samtal
Lägg till implementeringen i CallButton_Click
för att starta olika typer av anrop med objektet teamsCallAgent
vi skapade och koppla in RemoteParticipantsUpdated
och StateChanged
händelsehanterare för TeamsCommunicationCall
objektet.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
teamsCall = await StartCteCallAsync(callString);
if (teamsCall != null)
{
teamsCall.StateChanged += OnStateChangedAsync;
}
}
Avsluta samtal
Avsluta det aktuella samtalet när Hang up
knappen klickas. Lägg till implementeringen i HangupButton_Click för att avsluta ett anrop och stoppa förhandsgranskningen och videoströmmarna.
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 });
}
}
Växla ljudavstängning/ljudavstängning
Stäng av det utgående ljudet när Mute
knappen klickas. Lägg till implementeringen i MuteLocal_Click för att stänga av anropet.
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
}
}
Starta samtalet
När ett StartTeamsCallOptions
objekt har hämtats TeamsCallAgent
kan användas för att initiera Teams-anropet:
private async Task<TeamsCommunicationCall> StartCteCallAsync(string cteCallee)
{
var options = new StartTeamsCallOptions();
var teamsCall = await this.teamsCallAgent.StartCallAsync( new MicrosoftTeamsUserCallIdentifier(cteCallee), options);
return call;
}
Acceptera ett inkommande samtal
TeamsIncomingCallReceived
händelsemottagaren har konfigurerats i SDK bootstrap-hjälpen InitCallAgentAndDeviceManagerAsync
.
this.teamsCallAgent.IncomingCallReceived += OnIncomingCallAsync;
Programmet har möjlighet att konfigurera hur det inkommande samtalet ska godkännas, till exempel video- och ljudströmstyper.
private async void OnIncomingCallAsync(object sender, TeamsIncomingCallReceivedEventArgs args)
{
var teamsIncomingCall = args.IncomingCall;
var acceptteamsCallOptions = new AcceptTeamsCallOptions() { };
teamsCall = await teamsIncomingCall.AcceptAsync(acceptteamsCallOptions);
teamsCall.StateChanged += OnStateChangedAsync;
}
Ansluta till ett Teams-samtal
Användaren kan också ansluta till ett befintligt anrop genom att skicka en länk
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
JoinTeamsCallOptions options = new JoinTeamsCallOptions();
TeamsCall call = await teamsCallAgent.JoinAsync(link, options);
Övervaka och svara på ändringshändelse för samtalstillstånd
StateChanged
händelsen på TeamsCommunicationCall
objektet utlöses när en pågående anropstransaktioner från ett tillstånd till ett annat. Programmet erbjuds möjligheter att återspegla tillståndsändringarna i användargränssnittet eller infoga affärslogik.
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;
}
}
}
Kör koden
Du kan skapa och köra koden i Visual Studio. För lösningsplattformar stöder ARM64
vi , x64
och x86
.
Du kan ringa ett utgående anrop genom att ange ett användar-ID i textfältet och klicka på Start Call/Join
knappen. Samtal 8:echo123
ansluter dig till en ekorobot. Den här funktionen är bra för att komma igång och verifiera att ljudenheterna fungerar.
Kom igång med Azure Communication Services med hjälp av Kommunikationstjänster som anropar SDK för att lägga till röst- och videosamtal 1:1 i din app. Du får lära dig hur du startar och svarar på ett samtal med hjälp av Azure Communication Services Calling SDK för Java.
Exempelkod
Om du vill gå vidare till slutet kan du ladda ned den här snabbstarten som ett exempel på GitHub.
Förutsättningar
- Ett Azure-konto med en aktiv prenumeration. Skapa ett konto utan kostnad.
- Android Studio för att skapa ditt Android-program.
- En distribuerad Communication Services-resurs. Skapa en Communication Services-resurs. Du måste registrera niska veze för den här snabbstarten.
- En användaråtkomsttoken för azure-kommunikationstjänsten.
- Hämta Teams tråd-ID till för anropsåtgärder med Hjälp av Graph Explorer. Läs mer om hur du skapar chatttråds-ID.
Konfigurera
Skapa en Android-app med en tom aktivitet
Från Android Studio väljer du Starta ett nytt Android Studio-projekt.
Välj projektmallen "Tom aktivitet" under "Telefon och surfplatta".
Välj Minsta SDK för "API 26: Android 8.0 (Oreo)" eller senare.
Installera -paketet
Leta upp din projektnivå build.gradle och se till att lägga mavenCentral()
till i listan över lagringsplatser under buildscript
och allprojects
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
I modulnivån build.gradle lägger du sedan till följande rader i avsnitten beroenden och 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'
...
}
Lägga till behörigheter i programmanifestet
För att kunna begära behörigheter som krävs för att göra ett anrop måste de deklareras i programmanifestet (app/src/main/AndroidManifest.xml
). Ersätt innehållet i filen med följande kod:
<?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>
Konfigurera layouten för appen
Två indata behövs: en textinmatning för samtals-ID:t och en knapp för att ringa samtalet. Dessa indata kan läggas till via designern eller genom att redigera xml-layouten. Skapa en knapp med ett ID call_button
för och en textinmatning på callee_id
. Gå till (app/src/main/res/layout/activity_main.xml
) och ersätt innehållet i filen med följande kod:
<?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>
Skapa huvudaktivitetens byggnadsställningar och bindningar
När layouten har skapats kan bindningarna läggas till samt den grundläggande scaffoldingen för aktiviteten. Aktiviteten hanterar begäran om körningsbehörigheter, skapar teams-samtalsagenten och anropar när knappen trycks ned. Var och en omfattas av sitt eget avsnitt. Metoden onCreate
åsidosättas för att anropa getAllPermissions
och createTeamsAgent
lägga till bindningar för anropsknappen. Den här händelsen inträffar bara en gång när aktiviteten skapas. Mer information onCreate
finns i guiden Förstå aktivitetslivscykeln.
Gå till MainActivity.java och ersätt innehållet med följande kod:
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
}
}
Begär behörigheter vid körning
För Android 6.0 och senare (API-nivå 23) och targetSdkVersion
23 eller senare beviljas behörigheter vid körning i stället för när appen installeras. För att stödja det getAllPermissions
kan implementeras för att anropa ActivityCompat.checkSelfPermission
och ActivityCompat.requestPermissions
för varje nödvändig behörighet.
/**
* 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);
}
}
Kommentar
När du utformar din app bör du tänka på när dessa behörigheter ska begäras. Behörigheter bör begäras när de behövs, inte i förväg. Mer information finns i Android-behörighetsguiden.
Objektmodell
Följande klasser och gränssnitt hanterar några av de viktigaste funktionerna i Azure Communication Services Calling SDK:
Name | beskrivning |
---|---|
CallClient |
CallClient är den viktigaste startpunkten för anropande SDK. |
TeamsCallAgent |
TeamsCallAgent Används för att starta och hantera anrop. |
TeamsCall |
Används TeamsCall för att representera ett Teams-samtal. |
CommunicationTokenCredential |
CommunicationTokenCredential Används som tokenautentiseringsuppgifter för att instansiera TeamsCallAgent . |
CommunicationIdentifier |
CommunicationIdentifier Används som en annan typ av deltagare som kan ingå i ett anrop. |
Skapa en agent från användarens åtkomsttoken
Med en användartoken kan en autentiserad samtalsagent instansieras. Vanligtvis genereras denna token från en tjänst med autentisering som är specifik för programmet. Mer information om token för användaråtkomst finns i guiden Användaråtkomsttoken .
För snabbstarten ersätter du <User_Access_Token>
med en användaråtkomsttoken som genererats för din Azure Communication Service-resurs.
/**
* 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();
}
}
Starta ett samtal med hjälp av samtalsagenten
Att ringa samtalet kan göras via teamens samtalsagent och kräver bara att du anger en lista över nummermottagarens ID:t och samtalsalternativen. För snabbstarten används standardalternativen för samtal utan video och ett enda samtals-ID från textinmatningen.
/**
* 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);
}
Besvara ett samtal
Att acceptera ett anrop kan göras med hjälp av teams-samtalsagenten med endast en referens till den aktuella kontexten.
public void acceptACall(TeamsIncomingCall teamsIncomingCall){
teamsIncomingCall.accept(this);
}
Ansluta till ett Teams-samtal
En användare kan ansluta till ett befintligt anrop genom att skicka en länk.
/**
* Join a call using a teams meeting link.
*/
public TeamsCall joinTeamsCall(TeamsCallAgent teamsCallAgent){
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
TeamsCall call = teamsCallAgent.join(this, link);
}
Ansluta till ett Teams-samtal med alternativ
Vi kan också ansluta till ett befintligt anrop med förinställda alternativ, till exempel att vara avstängda.
/**
* 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);
}
Konfigurera inkommande samtalslyssnare
För att kunna identifiera inkommande samtal och andra åtgärder som inte utförs av den här användaren måste lyssnarna konfigureras.
private TeamsIncomingCall teamsincomingCall;
teamsCallAgent.addOnIncomingCallListener(this::handleIncomingCall);
private void handleIncomingCall(TeamsIncomingCall incomingCall) {
this.teamsincomingCall = incomingCall;
}
Starta appen och anropa ekoroboten
Appen kan nu startas med knappen "Kör app" i verktygsfältet (Skift+F10). Kontrollera att du kan ringa samtal genom att anropa 8:echo123
. Ett förinspelat meddelande spelas upp och upprepar sedan meddelandet tillbaka till dig.
Kom igång med Azure Communication Services med hjälp av Communication Services anropande SDK för att lägga till ett på ett videosamtal i din app. Du lär dig hur du startar och svarar på ett videosamtal med hjälp av Azure Communication Services Calling SDK för iOS med hjälp av Teams-identitet.
Exempelkod
Om du vill gå vidare till slutet kan du ladda ned den här snabbstarten som ett exempel på GitHub.
Förutsättningar
- Skaffa ett Azure-konto med en aktiv prenumeration. Skapa ett konto utan kostnad.
- En Mac som kör Xcode, tillsammans med ett giltigt utvecklarcertifikat installerat i nyckelringen.
- Skapa en aktiv Communication Services-resurs. Skapa en Communication Services-resurs. Du måste registrera niska veze för den här snabbstarten.
- En användaråtkomsttoken för azure-kommunikationstjänsten.
- Hämta Teams tråd-ID till för anropsåtgärder med Hjälp av Graph Explorer. Läs mer om hur du skapar chatttråds-ID
Konfigurera
Skapa Xcode-projektet
I Xcode skapar du ett nytt iOS-projekt och väljer mallen Enkel vyapp. I den här självstudien används SwiftUI-ramverket, så du bör ange Språket till Swift och användargränssnittet till SwiftUI. Du kommer inte att skapa tester under den här snabbstarten. Avmarkera Inkludera tester.
Installera CocoaPods
Använd den här guiden för att installera CocoaPods på din Mac.
Installera paketet och beroenden med CocoaPods
Om du vill skapa en
Podfile
för ditt program öppnar du terminalen och navigerar till projektmappen och kör podd-init.Lägg till följande kod i
Podfile
och spara. Se SDK-supportversioner.
platform :ios, '13.0'
use_frameworks!
target 'VideoCallingQuickstart' do
pod 'AzureCommunicationCalling', '~> 2.10.0'
end
Kör poddinstallation.
.xcworkspace
Öppna med Xcode.
Begär åtkomst till mikrofonen och kameran
För att få åtkomst till enhetens mikrofon och kamera måste du uppdatera appens informationsegenskapslista med en NSMicrophoneUsageDescription
och NSCameraUsageDescription
. Du anger det associerade värdet till en sträng som innehåller den dialogruta som systemet använder för att begära åtkomst från användaren.
Högerklicka på posten i Info.plist
projektträdet och välj Öppna som > källkod. Lägg till följande rader i avsnittet på den översta nivån <dict>
och spara sedan filen.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for video calling</string>
Konfigurera appramverket
Öppna projektets ContentView.swift
fil och lägg till en importdeklaration överst i filen för att importera AzureCommunicationCalling
biblioteket och AVFoundation
. AVFoundation används för att samla in ljudbehörighet från kod.
import AzureCommunicationCalling
import AVFoundation
Objektmodell
Följande klasser och gränssnitt hanterar några av de viktigaste funktionerna i Azure Communication Services Calling SDK för iOS.
Name | beskrivning |
---|---|
CallClient |
CallClient är den viktigaste startpunkten för anropande SDK. |
TeamsCallAgent |
TeamsCallAgent Används för att starta och hantera anrop. |
TeamsIncomingCall |
TeamsIncomingCall Används för att acceptera eller avvisa inkommande teamsamtal. |
CommunicationTokenCredential |
CommunicationTokenCredential Används som tokenautentiseringsuppgifter för att instansiera TeamsCallAgent . |
CommunicationIdentifier |
CommunicationIdentifier Används för att representera användarens identitet, vilket kan vara något av följande alternativ: CommunicationUserIdentifier , PhoneNumberIdentifier eller CallingApplication . |
Skapa Teams-samtalsagenten
Ersätt implementeringen av ContentView struct
med några enkla användargränssnittskontroller som gör att en användare kan initiera och avsluta ett anrop. Vi lägger till affärslogik i dessa kontroller i den här snabbstarten.
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()
}
}
Autentisera klienten
För att initiera en TeamsCallAgent
instans behöver du en användaråtkomsttoken som gör att den kan göra och ta emot anrop. Se dokumentationen för användaråtkomsttoken om du inte har någon tillgänglig token.
När du har en token lägger du till följande kod i återanropet onAppear
i ContentView.swift
. Du måste ersätta <USER ACCESS TOKEN>
med en giltig användaråtkomsttoken för resursen:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
Initiera Teams CallAgent och åtkomst Upravljač uređajima
Om du vill skapa en TeamsCallAgent
instans från en CallClient
använder du metoden callClient.createTeamsCallAgent
som asynkront returnerar ett TeamsCallAgent
objekt när det har initierats. DeviceManager
låter dig räkna upp lokala enheter som kan användas i ett anrop för att överföra ljud-/videoströmmar. Du kan också begära behörighet från en användare för att få åtkomst till mikrofon/kamera.
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")
}
}
}
}
Be om behörigheter
Vi måste lägga till följande kod i återanropet onAppear
för att be om behörigheter för ljud och video.
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
AVCaptureDevice.requestAccess(for: .video) { (videoGranted) in
/* NO OPERATION */
}
}
}
Ringa ett utgående samtal
Metoden startCall
anges som den åtgärd som utförs när knappen Starta samtal knackas. I den här snabbstarten är utgående samtal endast ljud som standard. För att starta ett samtal med video måste vi ställa in VideoOptions
med LocalVideoStream
och skicka det med startCallOptions
för att ange inledande alternativ för samtalet.
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.
}
Ansluta till Teams-möte
Med join
metoden kan användaren ansluta till ett teams-möte.
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
och RemotePariticipantObserver
används för att hantera mellansamtalshändelser och fjärranslutna deltagare. Vi ställer in observatörerna setTeamsCallAndObserver
i funktionen.
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")
}
}
Besvara ett inkommande samtal
För att besvara ett inkommande samtal implementerar du en TeamsIncomingCallHandler
för att visa den inkommande samtalsbanderollen för att besvara eller avvisa samtalet. Placera följande implementering i 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
}
}
}
Vi måste skapa en instans av TeamsIncomingCallHandler
genom att lägga till följande kod i återanropet onAppear
i ContentView.swift
:
Ange ett ombud till TeamsCallAgent
efter att ha TeamsCallAgent
skapats:
self.teamsCallAgent!.delegate = incomingCallHandler
När det finns ett inkommande samtal TeamsIncomingCallHandler
anropar funktionen showIncomingCallBanner
för att visa answer
och decline
knapp.
func showIncomingCallBanner(_ incomingCall: TeamsIncomingCall) {
self.teamsIncomingCall = incomingCall
}
Åtgärderna som är kopplade till answer
och decline
implementeras som följande kod. För att besvara samtalet med video måste vi aktivera den lokala videon och ange alternativen AcceptCallOptions
för med 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.
}
}
Prenumerera på händelser
Vi kan implementera en TeamsCallObserver
klass för att prenumerera på en samling händelser som ska meddelas när värden ändras under anropet.
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
}
}
}
Kör koden
Du kan skapa och köra appen i iOS-simulatorn genom att välja Produktkörning > eller med hjälp av kortkommandot (⌘-R).
Rensa resurser
Om du vill rensa och ta bort en Communication Services-prenumeration kan du ta bort resursen eller resursgruppen. Om du tar bort resursgruppen tas även alla andra resurser som är associerade med den bort. Läs mer om att rensa resurser.
Nästa steg
Mer information finns i följande artiklar:
- Kolla in vårt webbsamtalsexempel
- Lär dig mer om funktioner för samtals-SDK
- Läs mer om hur samtal fungerar