Mulai Cepat: Menambahkan panggilan video 1:1 sebagai pengguna Teams ke aplikasi Anda
Mulai menggunakan Azure Communication Services dengan menggunakan SDK panggilan Communication Services untuk menambahkan panggilan suara &video 1:1 ke aplikasi Anda. Anda akan mempelajari cara memulai dan menjawab panggilan menggunakan SDK Panggilan Azure Communication Services untuk JavaScript.
Kode Sampel
Jika Anda ingin melompati ke bagian akhir, Anda dapat mengunduh mulai cepat ini sebagai sampel di GitHub.
Prasyarat
- Dapatkan akun Azure dengan langganan aktif. Buat akun secara gratis.
- Kau harus memiliki Node.js 18. Anda dapat menggunakan alat penginstal msi untuk menginstalnya.
- Buat sumber daya Azure Communication Services. Buat sumber daya Azure Communication Services.
- Buat Token Akses Pengguna untuk membuat instans klien panggilan. Pelajari cara membuat dan mengelola token akses pengguna.
- Dapatkan ID utas Teams ke untuk operasi panggilan menggunakan Graph Explorer. Baca selengkapnya tentang cara membuat ID utas obrolan.
Menyiapkan
Membuat aplikasi Node.js baru
Buka terminal atau jendela perintah Anda, buat direktori baru untuk aplikasi Anda, dan navigasikan ke direktori.
mkdir calling-quickstart && cd calling-quickstart
Jalankan npm init -y
untuk membuat file package.json dengan pengaturan default.
npm init -y
Pasang paket
Gunakan perintah npm install
untuk memasang Azure Communication Services Calling SDK untuk JavaScript.
Penting
Mulai cepat ini menggunakan versi SDK Panggilan Azure Communication Services terbaru.
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Menyiapkan kerangka kerja aplikasi
Mulai cepat ini menggunakan webpack untuk memaketkan aset aplikasi. Jalankan perintah berikut untuk menginstal paket npm webpack
, webpack-cli
, dan webpack-dev-server
, serta cantumkan sebagai dependensi pengembangan di package.json
Anda:
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
Buat file index.html
di direktori akar proyek Anda. Kami akan menggunakan file ini untuk mengonfigurasi tata letak dasar yang akan memungkinkan pengguna melakukan panggilan video pribadi (1:1).
Berikut kodenya:
<!-- 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>
Model Objek Web SDK Panggilan Azure Communication Services
Kelas dan antarmuka berikut menangani beberapa fitur utama SDK Panggilan Azure Communication Services:
Nama | Deskripsi |
---|---|
CallClient |
Titik masuk utama ke Calling SDK. |
AzureCommunicationTokenCredential |
Menerapkan antarmuka CommunicationTokenCredential , yang digunakan untuk membuat instans teamsCallAgent . |
TeamsCallAgent |
Digunakan untuk memulai dan mengelola panggilan Teams. |
DeviceManager |
Digunakan untuk mengelola perangkat media. |
TeamsCall |
Digunakan untuk mewakili Panggilan Teams |
LocalVideoStream |
Digunakan untuk membuat aliran video lokal untuk perangkat kamera pada sistem lokal. |
RemoteParticipant |
Digunakan untuk mewakili peserta jarak jauh di Panggilan |
RemoteVideoStream |
Digunakan untuk mewakili aliran video jarak jauh dari Peserta Jarak Jauh. |
Buat file di direktori akar proyek Anda yang bernama index.js
guna memuat logika aplikasi untuk mulai cepat ini. Tambahkan kode berikut ke 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();
});
Menambahkan kode server lokal webpack
Buat file di direktori akar proyek Anda yang disebut webpack.config.js untuk memuat logika server lokal untuk mulai cepat ini. Tambahkan kode berikut ke 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'
]
}),
]
};
Menjalankan kode
Gunakan webpack-dev-server
untuk membuat dan menjalankan aplikasi Anda. Jalankan perintah berikut untuk membundel host aplikasi di webserver lokal:
`npx webpack serve --config webpack.config.js`
Buka browser Anda dan pada dua tab navigasi ke http://localhost:8080/. Tab harus menampilkan hasil serupa seperti gambar berikut:
Pada tab pertama, masukkan token akses pengguna yang valid. Pada tab kedua, masukkan token akses pengguna lain yang valid. Lihat dokumentasi token akses pengguna jika Anda belum memiliki token akses yang tersedia untuk digunakan. Pada kedua tab, klik tombol "Inisialisasi Agen Panggilan". Tab harus menampilkan hasil serupa seperti gambar berikut:
Pada tab pertama, masukkan identitas pengguna Azure Communication Services dari tab kedua, dan pilih tombol "Mulai Panggilan". Tab pertama akan memulai panggilan keluar ke tab kedua, dan tombol "Terima Panggilan" tab kedua akan diaktifkan:
Dari tab kedua, pilih tombol "Terima Panggilan". Panggilan akan dijawab dan dihubungkan. Tab harus menampilkan hasil serupa seperti gambar berikut:
Kedua tab sekarang berhasil dalam panggilan video 1:1. Kedua pengguna dapat mendengar audio satu sama lain dan melihat aliran video lainnya.
Mulai menggunakan Azure Communication Services dengan menggunakan SDK panggilan Communication Services untuk menambahkan panggilan suara &video 1:1 ke aplikasi Anda. Anda mempelajari cara memulai dan menjawab panggilan menggunakan Azure Communication Services Calling SDK untuk Windows.
Kode Sampel
Jika Anda ingin melompati ke bagian akhir, Anda dapat mengunduh mulai cepat ini sebagai sampel di GitHub.
Prasyarat
Untuk menyelesaikan tutorial ini, Anda memerlukan prasyarat berikut:
- Akun Azure dengan langganan aktif. Buat akun secara gratis.
- Instal Visual Studio 2022 dengan beban kerja pengembangan Platform Windows Universal.
- Sumber daya Communication Services yang disebarkan. Buat sumber daya Azure Communication Services. Anda perlu merekam string koneksi Anda untuk mulai cepat ini.
- Token Akses Pengguna untuk Azure Communication Service.
- Dapatkan ID utas Teams ke untuk operasi panggilan menggunakan Graph Explorer. Baca selengkapnya tentang cara membuat ID utas obrolan.
Menyiapkan
Membuat proyek
Di Visual Studio, buat proyek baru dengan templat Blank App (Universal Windows) untuk menyiapkan aplikasi Universal Windows Platform (UWP) satu halaman.
Pasang paket
Pilih kanan proyek Anda dan buka Manage Nuget Packages
untuk menginstal Azure.Communication.Calling.WindowsClient
1.2.0-beta.1 atau lebih unggul. Pastikan Sertakan Yang Telah Dilepas telah dicentang.
Meminta akses
Package.appxmanifest
Buka dan pilih Capabilities
.
Periksa Internet (Client)
dan Internet (Client & Server)
untuk mendapatkan akses masuk dan keluar ke Internet. Periksa Microphone
untuk mengakses umpan audio mikrofon, dan Webcam
untuk mengakses umpan video kamera.
Menyiapkan kerangka kerja aplikasi
Kita perlu mengonfigurasi tata letak dasar untuk melampirkan logika. Untuk melakukan panggilan keluar, kita perlu TextBox
memberikan ID Pengguna dari penerima panggilan. Kita juga butuh tombol Start/Join call
dan tombol Hang up
. Mute
Kotak centang dan BackgroundBlur
juga disertakan dalam sampel ini untuk menunjukkan fitur mengubah status audio dan efek video.
Buka MainPage.xaml
proyek Anda dan tambahkan node Grid
ke 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>
Buka MainPage.xaml.cs
dan ganti konten dengan implementasi berikut:
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
}
}
Model objek
Tabel berikutnya mencantumkan kelas dan antarmuka menangani beberapa fitur utama SDK Panggilan Azure Communication Services:
Nama | Deskripsi |
---|---|
CallClient |
CallClient adalah titik masuk utama ke SDK Panggilan. |
TeamsCallAgent |
TeamsCallAgent digunakan untuk memulai dan mengelola panggilan. |
TeamsCommunicationCall |
TeamsCommunicationCall digunakan untuk mengelola panggilan yang sedang berlangsung. |
CallTokenCredential |
CallTokenCredential digunakan sebagai kredensial token untuk membuat instans TeamsCallAgent . |
CallIdentifier |
CallIdentifier digunakan untuk mewakili identitas pengguna, yang dapat menjadi salah satu opsi berikut: MicrosoftTeamsUserCallIdentifier , , UserCallIdentifier PhoneNumberCallIdentifier dll. |
Mengautentikasi klien
Menginisialisasi TeamsCallAgent
instans dengan Token Akses Pengguna yang memungkinkan kami melakukan dan menerima panggilan, dan secara opsional mendapatkan instans DeviceManager untuk mengkueri konfigurasi perangkat klien.
Dalam kode, ganti <AUTHENTICATION_TOKEN>
dengan Token Akses Pengguna. Lihat dokumentasi token akses pengguna jika Anda belum memiliki token yang tersedia.
Tambahkan InitCallAgentAndDeviceManagerAsync
fungsi, yang mem-bootstrap SDK. Pembantu ini dapat disesuaikan untuk memenuhi persyaratan aplikasi Anda.
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;
}
Memulai panggilan
Tambahkan implementasi ke CallButton_Click
untuk memulai berbagai jenis panggilan dengan objek yang teamsCallAgent
kami buat, dan menghubungkan RemoteParticipantsUpdated
dan StateChanged
penanganan aktivitas pada TeamsCommunicationCall
objek.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
teamsCall = await StartCteCallAsync(callString);
if (teamsCall != null)
{
teamsCall.StateChanged += OnStateChangedAsync;
}
}
Mengakhiri panggilan
Akhiri panggilan saat ini ketika tombol Hang up
diklik. Tambahkan implementasi ke HangupButton_Click untuk mengakhiri panggilan, dan hentikan pratinjau dan streaming video.
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 });
}
}
Matikan suara/nyalakan suara pada audio
Matikan suara audio keluar saat tombol Mute
diklik. Tambahkan implementasi ke MuteLocal_Click untuk mematikan suara panggilan.
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
}
}
Memulai panggilan
StartTeamsCallOptions
Setelah objek diperoleh, TeamsCallAgent
dapat digunakan untuk memulai panggilan Teams:
private async Task<TeamsCommunicationCall> StartCteCallAsync(string cteCallee)
{
var options = new StartTeamsCallOptions();
var teamsCall = await this.teamsCallAgent.StartCallAsync( new MicrosoftTeamsUserCallIdentifier(cteCallee), options);
return call;
}
Menerima panggilan masuk
TeamsIncomingCallReceived
sink peristiwa disiapkan di pembantu bootstrap InitCallAgentAndDeviceManagerAsync
SDK .
this.teamsCallAgent.IncomingCallReceived += OnIncomingCallAsync;
Aplikasi memiliki kesempatan untuk mengonfigurasi bagaimana panggilan masuk harus diterima, seperti jenis streaming video dan audio.
private async void OnIncomingCallAsync(object sender, TeamsIncomingCallReceivedEventArgs args)
{
var teamsIncomingCall = args.IncomingCall;
var acceptteamsCallOptions = new AcceptTeamsCallOptions() { };
teamsCall = await teamsIncomingCall.AcceptAsync(acceptteamsCallOptions);
teamsCall.StateChanged += OnStateChangedAsync;
}
Bergabung dengan Panggilan Teams
Pengguna juga dapat bergabung dengan panggilan yang ada dengan meneruskan tautan
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
JoinTeamsCallOptions options = new JoinTeamsCallOptions();
TeamsCall call = await teamsCallAgent.JoinAsync(link, options);
Memantau dan merespons peristiwa perubahan status panggilan
StateChanged
peristiwa pada TeamsCommunicationCall
objek diaktifkan ketika transaksi panggilan yang sedang berlangsung dari satu status ke status lainnya. Aplikasi ditawarkan peluang untuk mencerminkan perubahan status pada UI atau menyisipkan logika bisnis.
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;
}
}
}
Menjalankan kode
Anda dapat membangun dan menjalankan kode di Visual Studio. Untuk platform solusi, kami mendukung ARM64
, x64
dan x86
.
Anda dapat melakukan panggilan keluar dengan memberikan ID pengguna di bidang teks dan mengklik tombol Start Call/Join
. Memanggil 8:echo123
menghubungkan Anda dengan bot echo, fitur ini bagus untuk memulai dan memverifikasi perangkat audio Anda berfungsi.
Mulai menggunakan Azure Communication Services dengan menggunakan SDK panggilan Communication Services untuk menambahkan panggilan suara &video 1:1 ke aplikasi Anda. Anda akan mempelajari cara memulai dan menjawab panggilan menggunakan SDK Panggilan Azure Communication Services untuk Java.
Kode Sampel
Jika Anda ingin melompati ke bagian akhir, Anda dapat mengunduh mulai cepat ini sebagai sampel di GitHub.
Prasyarat
- Akun Azure dengan langganan aktif. Buat akun secara gratis.
- Android Studio, untuk membuat aplikasi Android Anda.
- Sumber daya Communication Services yang disebarkan. Buat sumber daya Azure Communication Services. Anda perlu merekam string koneksi Anda untuk mulai cepat ini.
- Token Akses Pengguna untuk Azure Communication Service.
- Dapatkan ID utas Teams ke untuk operasi panggilan menggunakan Graph Explorer. Baca selengkapnya tentang cara membuat ID utas obrolan.
Menyiapkan
Membuat aplikasi Android dengan aktivitas kosong
Dari Android Studio, pilih Mulai proyek Android Studio baru.
Pilih templat proyek "Aktivitas Kosong" di bagian "Ponsel dan Tablet".
Pilih SDK Minimum "API 26: Android 8.0 (Oreo)" atau yang lebih tinggi.
Pasang paket
Temukan build.gradle tingkat proyek Anda dan pastikan untuk menambahkan mavenCentral()
ke daftar repositori di bawah buildscript
dan allprojects
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Kemudian, di build.gradle tingkat modul Anda, tambahkan baris berikut ke dependensi dan bagian 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'
...
}
Menambahkan izin ke manifes aplikasi
Untuk meminta izin yang diperlukan untuk melakukan panggilan, mereka harus dideklarasikan dalam Manifes Aplikasi (app/src/main/AndroidManifest.xml
). Ganti konten file dengan kode berikut:
<?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>
Menyiapkan tata letak untuk aplikasi
Dua input diperlukan: input teks untuk ID penerima panggilan, dan tombol untuk melakukan panggilan. Input ini dapat ditambahkan melalui perancang atau dengan mengedit xml tata letak. Buat tombol dengan ID call_button
dan input teks callee_id
. Navigasi ke (app/src/main/res/layout/activity_main.xml
) dan ganti konten file dengan kode berikut:
<?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>
Membuat perancah dan pengikatan aktivitas utama
Dengan tata letak yang sudah dibuat, pengikatan beserta perancah dasar aktivitas dapat ditambahkan. Aktivitas menangani permintaan izin runtime, membuat agen panggilan tim, dan melakukan panggilan saat tombol ditekan. Masing-masing tercakup dalam bagiannya sendiri. Metode onCreate
ini ditimpa untuk memanggil getAllPermissions
dan createTeamsAgent
dan menambahkan pengikatan untuk tombol panggilan. Peristiwa ini hanya terjadi sekali ketika aktivitas dibuat. Untuk informasi selengkapnya, pada onCreate
, lihat panduan Memahami Siklus Hidup Aktivitas.
Buka MainActivity.java dan ganti konten dengan kode berikut:
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
}
}
Meminta izin saat runtime
Untuk Android 6.0 dan yang lebih tinggi (API level 23) dan targetSdkVersion
23 atau yang lebih tinggi, izin diberikan saat runtime, bukan saat aplikasi diinstal. Untuk mendukungnya, getAllPermissions
dapat diimplementasikan untuk memanggil ActivityCompat.checkSelfPermission
dan ActivityCompat.requestPermissions
untuk setiap izin yang diperlukan.
/**
* 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);
}
}
Catatan
Saat merancang aplikasi Anda, pertimbangkan kapan izin ini harus diminta. Izin harus diminta saat dibutuhkan, bukan sebelumnya. Untuk informasi selengkapnya, lihat, Panduan Izin Android.
Model objek
Kelas dan antarmuka berikut menghandel beberapa fitur utama Azure Communication Services Calling SDK:
Nama | Deskripsi |
---|---|
CallClient |
CallClient adalah titik masuk utama ke SDK Panggilan. |
TeamsCallAgent |
TeamsCallAgent digunakan untuk memulai dan mengelola panggilan. |
TeamsCall |
yang TeamsCall digunakan untuk mewakili Panggilan Teams. |
CommunicationTokenCredential |
CommunicationTokenCredential digunakan sebagai kredensial token untuk membuat instans TeamsCallAgent . |
CommunicationIdentifier |
CommunicationIdentifier digunakan sebagai jenis peserta yang berbeda yang dapat menjadi bagian dari panggilan. |
Membuat agen dari token akses pengguna
Dengan token pengguna, agen panggilan terautentikasi dapat diinstansiasi. Umumnya token ini dihasilkan dari layanan dengan autentikasi khusus untuk aplikasi. Untuk informasi selengkapnya tentang token akses pengguna, periksa panduan Token Akses Pengguna.
Untuk mulai cepat, ganti <User_Access_Token>
dengan token akses pengguna yang dihasilkan untuk sumber daya Azure Communication Service Anda.
/**
* 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();
}
}
Memulai panggilan menggunakan agen panggilan
Menempatkan panggilan dapat dilakukan melalui agen panggilan tim, dan hanya perlu menyediakan daftar ID penerima panggilan dan opsi panggilan. Untuk mulai cepat, opsi panggilan default tanpa video dan SATU ID penerima panggilan dari input teks digunakan.
/**
* 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);
}
Menjawab Panggilan
Menerima panggilan dapat dilakukan menggunakan agen panggilan teams hanya menggunakan referensi ke konteks saat ini.
public void acceptACall(TeamsIncomingCall teamsIncomingCall){
teamsIncomingCall.accept(this);
}
Bergabung dengan Panggilan Teams
Pengguna dapat bergabung dengan panggilan yang ada dengan meneruskan tautan.
/**
* Join a call using a teams meeting link.
*/
public TeamsCall joinTeamsCall(TeamsCallAgent teamsCallAgent){
TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
TeamsCall call = teamsCallAgent.join(this, link);
}
Bergabung dengan Panggilan Teams dengan opsi
Kita juga dapat bergabung dengan panggilan yang ada dengan opsi prasetel, seperti dibisukan.
/**
* 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);
}
Menyiapkan Pendengar Panggilan Masuk
Agar dapat mendeteksi panggilan masuk dan tindakan lain yang tidak dilakukan oleh pengguna ini, pendengar harus disiapkan.
private TeamsIncomingCall teamsincomingCall;
teamsCallAgent.addOnIncomingCallListener(this::handleIncomingCall);
private void handleIncomingCall(TeamsIncomingCall incomingCall) {
this.teamsincomingCall = incomingCall;
}
Meluncurkan aplikasi dan memanggil bot echo
Aplikasi kini dapat diluncurkan menggunakan tombol "Jalankan Aplikasi" pada toolbar (Shift+F10). Verifikasi bahwa Anda dapat melakukan panggilan dengan memanggil 8:echo123
. Pesan yang direkam sebelumnya diputar lalu ulangi pesan Anda kembali kepada Anda.
Mulai menggunakan Azure Communication Services dengan menggunakan SDK panggilan Communication Services untuk menambahkan satu pada satu panggilan video ke aplikasi Anda. Anda mempelajari cara memulai dan menjawab panggilan video menggunakan Azure Communication Services Calling SDK untuk iOS menggunakan identitas Teams.
Kode Sampel
Jika Anda ingin melompati ke bagian akhir, Anda dapat mengunduh mulai cepat ini sebagai sampel di GitHub.
Prasyarat
- Dapatkan akun Azure dengan langganan aktif. Buat akun secara gratis.
- Mac yang menjalankan Xcode, bersama dengan sertifikat pengembang valid yang diinstal ke rantai kunci Anda.
- Buat sumber daya Azure Communication Services. Buat sumber daya Azure Communication Services. Anda perlu merekam string koneksi Anda untuk mulai cepat ini.
- Token Akses Pengguna untuk Azure Communication Service.
- Dapatkan ID utas Teams ke untuk operasi panggilan menggunakan Graph Explorer. Baca selengkapnya tentang cara membuat ID utas obrolan
Menyiapkan
Membuat proyek Xcode
Di Xcode, buat proyek iOS baru dan pilih templat Aplikasi Tampilan Tunggal. Tutorial ini menggunakan kerangka kerja SwiftUI, jadi Anda harus mengatur Bahasa ke Swift dan Antarmuka Pengguna ke SwiftUI. Anda tidak akan membuat tes selama mulai cepat ini. Jangan ragu untuk menghapus centang Sertakan Pengujian.
Menginstal CocoaPods
Gunakan panduan ini untuk menginstal CocoaPods di Mac Anda.
Memasang paket dan dependensi dengan CocoaPods
Untuk membuat
Podfile
untuk aplikasi Anda, buka terminal dan navigasikan ke folder proyek dan jalankan init pod.Tambahkan kode berikut ke
Podfile
dan simpan. Lihat versi dukungan SDK.
platform :ios, '13.0'
use_frameworks!
target 'VideoCallingQuickstart' do
pod 'AzureCommunicationCalling', '~> 2.10.0'
end
Jalankan penginstalan pod.
Buka
.xcworkspace
dengan Xcode.
Minta akses ke mikrofon dan kamera
Untuk mengakses mikrofon dan kamera perangkat, Anda perlu memperbarui aplikasi Daftar Properti Informasi denganNSMicrophoneUsageDescription
dan NSCameraUsageDescription
. Anda mengatur nilai terkait ke string yang menyertakan dialog yang digunakan sistem untuk meminta akses dari pengguna.
Info.plist
Klik kanan entri pohon proyek dan pilih Buka Sebagai > Kode Sumber. Tambahkan baris berikut ke bagian <dict>
tingkat atas, lalu simpan file.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for video calling</string>
Menyiapkan kerangka kerja aplikasi
Buka file proyek ContentView.swift
Anda dan tambahkan deklarasi impor ke bagian atas file untuk mengimpor pustaka AzureCommunicationCalling
dan AVFoundation
. AVFoundation digunakan untuk mengambil izin akses audio dari kode.
import AzureCommunicationCalling
import AVFoundation
Model objek
Kelas dan antarmuka berikut menangani beberapa fitur utama Azure Communication Services Calling SDK for iOS.
Nama | Deskripsi |
---|---|
CallClient |
CallClient adalah titik masuk utama ke SDK Panggilan. |
TeamsCallAgent |
TeamsCallAgent digunakan untuk memulai dan mengelola panggilan. |
TeamsIncomingCall |
TeamsIncomingCall digunakan untuk menerima atau menolak panggilan tim masuk. |
CommunicationTokenCredential |
CommunicationTokenCredential digunakan sebagai kredensial token untuk membuat instans TeamsCallAgent . |
CommunicationIdentifier |
CommunicationIdentifier digunakan untuk mewakili identitas pengguna, yang dapat menjadi salah satu opsi berikut: CommunicationUserIdentifier , PhoneNumberIdentifier atau CallingApplication . |
Membuat Agen Panggilan Teams
Ganti implementasi ContentView struct
dengan beberapa kontrol antarmuka pengguna sederhana yang memungkinkan pengguna memulai dan mengakhiri panggilan. Kami menambahkan logika bisnis ke kontrol ini dalam mulai cepat ini.
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()
}
}
Mengautentikasi klien
Untuk menginisialisasi TeamsCallAgent
instans, Anda memerlukan Token Akses Pengguna yang memungkinkannya melakukan, dan menerima panggilan. Lihat dokumentasi token akses pengguna, jika Anda tidak memiliki token yang tersedia.
Setelah Anda memiliki token, Tambahkan kode berikut ke onAppear
panggilan balik di ContentView.swift
. Anda perlu mengganti <USER ACCESS TOKEN>
dengan token akses pengguna yang valid untuk sumber daya Anda:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
Menginisialisasi CallAgent Teams dan mengakses Device Manager
Untuk membuat TeamsCallAgent
instans dari CallClient
, gunakan callClient.createTeamsCallAgent
metode yang secara asinkron mengembalikan TeamsCallAgent
objek setelah diinisialisasi. DeviceManager
memungkinkan Anda menghitung perangkat lokal yang dapat digunakan dalam panggilan untuk mengirimkan aliran audio/video. DeviceManager juga memungkinkan Anda untuk meminta izin akses dari pengguna untuk mengakses 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")
}
}
}
}
Meminta izin
Kita perlu menambahkan kode berikut ke panggilan balik onAppear
guna meminta izin audio dan video.
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
AVCaptureDevice.requestAccess(for: .video) { (videoGranted) in
/* NO OPERATION */
}
}
}
Melakukan panggilan keluar
Metode startCall
ini diatur sebagai tindakan yang dilakukan saat tombol Mulai Panggilan diketuk. Dalam mulai cepat ini, panggilan keluar hanya audio secara default. Untuk memulai panggilan dengan video, kita perlu mengatur VideoOptions
dengan LocalVideoStream
dan meneruskannya dengan startCallOptions
untuk mengatur opsi awal untuk panggilan.
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.
}
Bergabung dengan rapat Teams
Metode ini join
memungkinkan pengguna untuk bergabung dalam rapat tim.
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
dan RemotePariticipantObserver
digunakan untuk mengelola peristiwa tengah panggilan dan peserta jarak jauh. Kami mengatur pengamat dalam setTeamsCallAndObserver
fungsi .
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")
}
}
Menjawab panggilan masuk
Untuk menjawab panggilan masuk, implementasikan TeamsIncomingCallHandler
untuk menampilkan spanduk panggilan masuk untuk menjawab atau menolak panggilan. Masukkan implementasi berikut ke dalam 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
}
}
}
Kita perlu membuat instans TeamsIncomingCallHandler
dengan menambahkan kode berikut ke onAppear
panggilan balik di ContentView.swift
:
Atur delegasi ke TeamsCallAgent
setelah TeamsCallAgent
berhasil dibuat:
self.teamsCallAgent!.delegate = incomingCallHandler
Setelah ada panggilan masuk, TeamsIncomingCallHandler
memanggil fungsi showIncomingCallBanner
untuk ditampilkan answer
dan decline
tombol.
func showIncomingCallBanner(_ incomingCall: TeamsIncomingCall) {
self.teamsIncomingCall = incomingCall
}
Tindakan yang dilampirkan answer
dan decline
diimplementasikan sebagai kode berikut. Untuk menjawab panggilan dengan video, kita perlu mengaktifkan video lokal dan mengatur opsi AcceptCallOptions
dengan 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.
}
}
Berlangganan Peristiwa
Kita dapat menerapkan kelas TeamsCallObserver
untuk berlangganan koleksi acara yang akan diberi tahu ketika nilai berubah selama panggilan.
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
}
}
}
Menjalankan kode
Anda dapat membuat dan menjalankan aplikasi di simulator iOS dengan memilih Eksekusi Produk > atau dengan menggunakan pintasan keyboard (⌘-R).
Membersihkan sumber daya
Jika ingin membersihkan dan menghapus langganan Azure Communication Services, Anda bisa menghapus sumber daya atau grup sumber daya. Menghapus grup sumber daya juga menghapus sumber daya apa pun yang terkait dengannya. Pelajari selengkapnya tentang membersihkan sumber daya.
Langkah berikutnya
Untuk informasi lebih lanjut, baca artikel berikut:
- Lihat sampel panggilan web kami
- Pelajari kemampuan SDK Panggilan
- Pelajari lebih lanjut cara kerja panggilan