Aracılığıyla paylaş


Hızlı Başlangıç: Uygulamanıza Teams kullanıcısı olarak 1:1 görüntülü arama ekleme

uygulamanıza 1:1 sesli ve görüntülü arama eklemek için İletişim Hizmetleri çağrı SDK'sını kullanarak Azure İletişim Hizmetleri kullanmaya başlayın. JavaScript için Azure İletişim Hizmetleri Arama SDK'sını kullanarak bir aramayı başlatmayı ve yanıtlamayı öğreneceksiniz.

Örnek Kod

Sonuna atlamak isterseniz bu hızlı başlangıcı GitHub'da örnek olarak indirebilirsiniz.

Önkoşullar

Ayarlama

Yeni bir Node.js uygulaması oluşturma

Terminalinizi veya komut pencerenizi açın, uygulamanız için yeni bir dizin oluşturun ve dizine gidin.

mkdir calling-quickstart && cd calling-quickstart

Varsayılan ayarlarla bir package.json dosyası oluşturmak için komutunu çalıştırınnpm init -y.

npm init -y

paketini yükleyin

npm install JavaScript için Azure İletişim Hizmetleri Çağırma SDK'sını yüklemek için komutunu kullanın.

Önemli

Bu hızlı başlangıçta en son Azure İletişim Hizmetleri Çağırma SDK'sı sürümü kullanılmaktadır.

npm install @azure/communication-common --save
npm install @azure/communication-calling --save

Uygulama çerçevesini ayarlama

Bu hızlı başlangıçta uygulama varlıklarını paketlemek için webpack kullanılır. aşağıdaki komutu çalıştırarak webpack, webpack-cli ve webpack-dev-server npm paketlerini yükleyin ve bunları içinde geliştirme bağımlılıkları olarak listeleyin 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

Projenizin kök dizininde bir index.html dosya oluşturun. Bu dosyayı, kullanıcının 1:1 görüntülü aramasına izin verecek temel bir düzen yapılandırmak için kullanacağız.

Kod şu şekildedir:

<!-- index.html -->
<!DOCTYPE html>
<html>
    <head>
        <title>Azure Communication Services - Teams Calling Web Application</title>
    </head>
    <body>
        <h4>Azure Communication Services - Teams Calling Web Application</h4>
        <input id="user-access-token"
            type="text"
            placeholder="User access token"
            style="margin-bottom:1em; width: 500px;"/>
        <button id="initialize-teams-call-agent" type="button">Login</button>
        <br>
        <br>
        <input id="callee-teams-user-id"
            type="text"
            placeholder="Microsoft Teams callee's id (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)"
            style="margin-bottom:1em; width: 500px; display: block;"/>
        <button id="start-call-button" type="button" disabled="true">Start Call</button>
        <button id="hangup-call-button" type="button" disabled="true">Hang up Call</button>
        <button id="accept-call-button" type="button" disabled="true">Accept Call</button>
        <button id="start-video-button" type="button" disabled="true">Start Video</button>
        <button id="stop-video-button" type="button" disabled="true">Stop Video</button>
        <br>
        <br>
        <div id="connectedLabel" style="color: #13bb13;" hidden>Call is connected!</div>
        <br>
        <div id="remoteVideoContainer" style="width: 40%;" hidden>Remote participants' video streams:</div>
        <br>
        <div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div>
        <!-- points to the bundle generated from client.js -->
        <script src="./main.js"></script>
    </body>
</html>

Web SDK'sı Nesne modelini çağırmayı Azure İletişim Hizmetleri

Aşağıdaki sınıflar ve arabirimler, Azure İletişim Hizmetleri Çağırma SDK'sının bazı ana özelliklerini işler:

Veri Akışı Adı Açıklama
CallClient Çağrı SDK'sının ana giriş noktası.
AzureCommunicationTokenCredential örneğini CommunicationTokenCredential başlatmak için teamsCallAgentkullanılan arabirimini uygular.
TeamsCallAgent Teams çağrılarını başlatmak ve yönetmek için kullanılır.
DeviceManager Medya cihazlarını yönetmek için kullanılır.
TeamsCall Teams Aramasını temsil etmek için kullanılır
LocalVideoStream Yerel sistemdeki bir kamera cihazı için yerel video akışı oluşturmak için kullanılır.
RemoteParticipant Aramada uzak katılımcıyı temsil etmek için kullanılır
RemoteVideoStream Uzak Katılımcıdan uzak bir video akışını temsil etmek için kullanılır.

Projenizin kök dizininde, bu hızlı başlangıcın uygulama mantığını içerecek şekilde adlı index.js bir dosya oluşturun. index.js aşağıdaki kodu ekleyin:

// 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();
});

Webpack yerel sunucu kodunu ekleme

Bu hızlı başlangıcın yerel sunucu mantığını içermesi için projenizin kök dizininde webpack.config.js adlı bir dosya oluşturun. webpack.config.js aşağıdaki kodu ekleyin:

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'
            ]
        }),
    ]
};

Kodu çalıştırma

Uygulamanızı derlemek ve çalıştırmak için öğesini webpack-dev-server kullanın. Uygulama konağını yerel bir web sunucusu içinde paketlemek için aşağıdaki komutu çalıştırın:

`npx webpack serve --config webpack.config.js`

Tarayıcınızı açın ve iki sekmede http://localhost:8080/. Sekmelerde aşağıdaki görüntüye benzer bir sonuç gösterilmelidir: Varsayılan görünümde iki sekmeyi gösteren ekran görüntüsü. Her sekme farklı Teams kullanıcıları için kullanılır.

İlk sekmede geçerli bir kullanıcı erişim belirteci girin. İkinci sekmede başka bir geçerli kullanıcı erişim belirteci girin. Kullanılabilecek erişim belirteçleriniz yoksa kullanıcı erişim belirteci belgelerine bakın. Her iki sekmede de "Çağrı Aracısını Başlat" düğmelerine tıklayın. Sekmelerde aşağıdaki görüntüye benzer bir sonuç gösterilmelidir: Tarayıcı sekmesinde her Teams kullanıcısını başlatma adımlarını gösteren ekran görüntüsü.

İlk sekmede, ikinci sekmenin Azure İletişim Hizmetleri kullanıcı kimliğini girin ve "Aramayı Başlat" düğmesini seçin. İlk sekme, ikinci sekmeye giden çağrıyı başlatır ve ikinci sekmenin "Aramayı Kabul Et" düğmesi etkinleştirilir: Ekran görüntüsü, Teams kullanıcılarının SDK'yı başlatma deneyimini ve ikinci kullanıcıya çağrı başlatma adımlarını ve çağrıyı kabul etme yolunu gösterir.

İkinci sekmeden "Aramayı Kabul Et" düğmesini seçin. Arama yanıtlanır ve bağlanır. Sekmelerde aşağıdaki görüntüye benzer bir sonuç gösterilmelidir: Her biri ayrı sekmede oturum açmış iki Teams kullanıcısı arasında devam eden aramanın yer aldığı iki sekmeyi gösteren ekran görüntüsü.

Her iki sekme de artık 1:1 görüntülü aramasında başarılı bir şekilde sağlanır. Her iki kullanıcı da birbirlerinin sesini duyabilir ve birbirlerinin video akışını görebilir.

uygulamanıza 1:1 sesli ve görüntülü arama eklemek için İletişim Hizmetleri çağrı SDK'sını kullanarak Azure İletişim Hizmetleri kullanmaya başlayın. Windows için Azure İletişim Hizmetleri Arama SDK'sını kullanarak bir aramayı başlatmayı ve yanıtlamayı öğrenirsiniz.

Örnek Kod

Sonuna atlamak isterseniz bu hızlı başlangıcı GitHub'da örnek olarak indirebilirsiniz.

Önkoşullar

Bu öğreticiyi tamamlamak için aşağıdaki önkoşulları karşılamanız gerekir:

Ayarlama

Projeyi oluşturma

Visual Studio'da, tek sayfalı bir Evrensel Windows Platformu (UWP) uygulaması ayarlamak için Boş Uygulama (Evrensel Windows) şablonuyla yeni bir proje oluşturun.

Visual Studio'da Yeni UWP Projesi penceresini gösteren ekran görüntüsü.

paketini yükleyin

Sağ projenizi seçin ve 1.2.0-beta.1 veya üzerini yüklemeye Azure.Communication.Calling.WindowsClient gidinManage Nuget Packages. Önceden Dahil Et'in işaretli olduğundan emin olun.

Erişim isteğinde bulunma

adresine Package.appxmanifest gidin ve öğesini seçin Capabilities. Internet (Client & Server) İnternet'e gelen ve giden erişim elde etmek için ve öğesini denetleyinInternet (Client). Mikrofonun ses akışına erişmek ve Webcam kameranın video akışına erişmek için kontrol edinMicrophone.

Visual Studio'da İnternet ve Mikrofona erişim isteğinde bulunmayı gösteren ekran görüntüsü.

Uygulama çerçevesini ayarlama

Mantığımızı eklemek için temel bir düzen yapılandırmamız gerekir. Giden arama yapmak için, arayanın Kullanıcı Kimliğini sağlamamız gerekir TextBox . Ayrıca bir Start/Join call düğmeye ve bir düğmeye Hang up de ihtiyacımız var. Ses durumlarını ve BackgroundBlur video efektlerini açma/kapatma özelliklerini göstermek için bu örneğe A Mute ve onay kutuları da dahildir.

Projenizin öğesini MainPage.xaml açın ve düğümünü Grid öğesininize Pageekleyin:

<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 öğesini açın ve içeriğini aşağıdaki uygulamayla değiştirin:

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
    }
}

Nesne modeli

Sonraki tabloda sınıflar ve arabirimler, Azure İletişim Hizmetleri Çağırma SDK'sının bazı önemli özelliklerini işler:

Veri Akışı Adı Açıklama
CallClient CallClient, Çağrı SDK'sının ana giriş noktasıdır.
TeamsCallAgent TeamsCallAgent, çağrıları başlatmak ve yönetmek için kullanılır.
TeamsCommunicationCall TeamsCommunicationCall, devam eden bir çağrıyı yönetmek için kullanılır.
CallTokenCredential , CallTokenCredential örneğini TeamsCallAgentbaşlatmak için belirteç kimlik bilgisi olarak kullanılır.
CallIdentifier CallIdentifier, kullanıcının kimliğini temsil etmek için kullanılır. Bu, aşağıdaki seçeneklerden biri olabilir: MicrosoftTeamsUserCallIdentifier, UserCallIdentifier, PhoneNumberCallIdentifier vb.

İstemcinin kimliğini doğrulama

Çağrı yapmamıza ve almamıza olanak tanıyan bir Kullanıcı Erişim Belirteci ile bir TeamsCallAgent örnek başlatın ve isteğe bağlı olarak istemci cihaz yapılandırmalarını sorgulamak için bir DeviceManager örneği alın.

kodunda değerini bir Kullanıcı Erişim Belirteci ile değiştirin <AUTHENTICATION_TOKEN> . Henüz kullanılabilir bir belirteciniz yoksa kullanıcı erişim belirteci belgelerine bakın.

SDK'yı önyükleyen işlev ekleyin InitCallAgentAndDeviceManagerAsync . Bu yardımcı, uygulamanızın gereksinimlerini karşılayacak şekilde özelleştirilebilir.

        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;
        }

Arama başlatma

Oluşturduğumuz nesneyle teamsCallAgent çeşitli çağrı türlerini başlatmak ve nesne üzerinde bağlama RemoteParticipantsUpdated ve StateChanged olay işleyicileri oluşturmak için uygulamasına CallButton_Click TeamsCommunicationCall ekleyin.

        private async void CallButton_Click(object sender, RoutedEventArgs e)
        {
            var callString = CalleeTextBox.Text.Trim();

            teamsCall = await StartCteCallAsync(callString);
            if (teamsCall != null)
            {
                teamsCall.StateChanged += OnStateChangedAsync;
            }
        }

Aramayı sonlandırma

Düğmeye tıklandığında Hang up geçerli aramayı sonlandırın. Bir çağrıyı sonlandırmak ve önizleme ile video akışlarını durdurmak için uygulamayı HangupButton_Click ekleyin.

        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 });
            }
        }

Seste sesi kapatma/açma arasında geçiş

Düğmeye tıklandığında Mute giden sesin sesini kapat. Çağrının sesini kapatmak için uygulamayı MuteLocal_Click ekleyin.

        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
            }
        }

Aramayı başlatma

Bir StartTeamsCallOptions nesne elde edildikten sonra Teams TeamsCallAgent çağrısını başlatmak için kullanılabilir:

        private async Task<TeamsCommunicationCall> StartCteCallAsync(string cteCallee)
        {
            var options = new StartTeamsCallOptions();
            var teamsCall = await this.teamsCallAgent.StartCallAsync( new MicrosoftTeamsUserCallIdentifier(cteCallee), options);
            return call;
        }

Gelen aramayı kabul etme

TeamsIncomingCallReceived olay havuzu SDK bootstrap yardımcısında InitCallAgentAndDeviceManagerAsyncayarlanır.

    this.teamsCallAgent.IncomingCallReceived += OnIncomingCallAsync;

Uygulama, video ve ses akışı türleri gibi gelen çağrının nasıl kabul edilmesi gerektiğini yapılandırma fırsatına sahiptir.

        private async void OnIncomingCallAsync(object sender, TeamsIncomingCallReceivedEventArgs args)
        {
            var teamsIncomingCall = args.IncomingCall;

            var acceptteamsCallOptions = new AcceptTeamsCallOptions() { };

            teamsCall = await teamsIncomingCall.AcceptAsync(acceptteamsCallOptions);
            teamsCall.StateChanged += OnStateChangedAsync;
        }

Teams Çağrısına Katılma

Kullanıcı ayrıca bir bağlantı geçirerek mevcut bir aramaya katılabilir

TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
JoinTeamsCallOptions options = new JoinTeamsCallOptions();
TeamsCall call = await teamsCallAgent.JoinAsync(link, options);

Çağrı durumu değişiklik olayını izleme ve yanıtlama

StateChanged devam eden bir çağrı işlemi bir durumdan diğerine yapıldığında nesnedeki TeamsCommunicationCall olay tetiklenir. Uygulamaya kullanıcı arabirimindeki durum değişikliklerini yansıtma veya iş mantığı ekleme fırsatları sunulur.

        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;
                }
            }
        }

Kodu çalıştırma

Kodu Visual Studio'da derleyebilir ve çalıştırabilirsiniz. Çözüm platformları için , ve x86'yi x64 destekliyoruzARM64.

Metin alanında bir kullanıcı kimliği sağlayarak ve düğmeye tıklayarak Start Call/Join giden arama yapabilirsiniz. Arama 8:echo123 sizi bir yankı botuyla bağlar. Bu özellik, ses cihazlarınızın çalıştığını doğrulamak ve kullanmaya başlamak için harika bir özelliktir.

UWP hızlı başlangıç uygulamasını çalıştırmayı gösteren ekran görüntüsü

uygulamanıza 1:1 sesli ve görüntülü arama eklemek için İletişim Hizmetleri çağrı SDK'sını kullanarak Azure İletişim Hizmetleri kullanmaya başlayın. Java için Azure İletişim Hizmetleri Arama SDK'sını kullanarak aramayı başlatmayı ve yanıtlamayı öğreneceksiniz.

Örnek Kod

Sonuna atlamak isterseniz bu hızlı başlangıcı GitHub'da örnek olarak indirebilirsiniz.

Önkoşullar

Ayarlama

Boş etkinlik içeren bir Android uygulaması oluşturma

Android Studio'dan Yeni bir Android Studio projesi başlat'ı seçin.

Android Studio'da seçilen 'Yeni bir Android Studio Projesi başlat' düğmesini gösteren ekran görüntüsü.

"Telefon ve Tablet" altında "Boş Etkinlik" proje şablonunu seçin.

Proje Şablonu Ekranında 'Boş Etkinlik' seçeneğini gösteren ekran görüntüsü.

En Düşük "API 26: Android 8.0 (Oreo)" veya üzeri SDK'sı seçin.

Proje Şablonu Ekranı 2'de seçilen 'Boş Etkinlik' seçeneğini gösteren ekran görüntüsü.

paketini yükleyin

Build.gradle proje düzeyinizi bulun ve ve altındaki buildscript depolar listesine eklediğinizden mavenCentral() emin olunallprojects

buildscript {
    repositories {
    ...
        mavenCentral()
    ...
    }
}
allprojects {
    repositories {
    ...
        mavenCentral()
    ...
    }
}

Ardından, modül düzeyinizde build.gradle bağımlılıklara ve android bölümlerine aşağıdaki satırları ekleyin

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'
    ...
}

Uygulama bildirimine izin ekleme

Çağrı yapmak için gerekli izinleri istemek için, uygulama bildiriminde (app/src/main/AndroidManifest.xml ) bildirilmelidir. Dosyanın içeriğini aşağıdaki kodla değiştirin:

    <?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>
    

Uygulamanın düzenini ayarlama

İki giriş gereklidir: arayan kimliği için bir metin girişi ve çağrıyı yerleştirmek için bir düğme. Bu girişler tasarımcı aracılığıyla veya düzen xml'i düzenlenerek eklenebilir. kimliği call_button ve metin girişi callee_idolan bir düğme oluşturun. adresine gidin (app/src/main/res/layout/activity_main.xml) ve dosyanın içeriğini aşağıdaki kodla değiştirin:

<?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>

Ana etkinlik iskelesi ve bağlamaları oluşturma

Oluşturulan düzen ile bağlamaların yanı sıra etkinliğin temel iskelesi eklenebilir. Etkinlik, çalışma zamanı izinleri isteme, teams çağrı aracısını oluşturma ve düğmeye basıldığında aramayı yerleştirme işlemlerini işler. Her biri kendi bölümünde ele alınmıştır. ve onCreate çağrısı getAllPermissions düğmesinin bağlamalarını eklemek için yöntemi createTeamsAgent geçersiz kılınmış. Bu olay, etkinlik oluşturulduğunda yalnızca bir kez gerçekleşir. Daha fazla bilgi için, üzerinde onCreateEtkinlik Yaşam Döngüsünü Anlama kılavuzuna bakın.

MainActivity.java gidin ve içeriği aşağıdaki kodla değiştirin:

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
    }
}

Çalışma zamanında izin isteme

Android 6.0 ve üzeri (API düzeyi 23) ve targetSdkVersion 23 veya üzeri için, uygulama yüklendiğinde değil çalışma zamanında izinler verilir. Bunu desteklemek için çağrı getAllPermissions yapmak ActivityCompat.checkSelfPermission ve ActivityCompat.requestPermissions gerekli her izin için uygulanabilir.

/**
 * 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);
    }
}

Not

Uygulamanızı tasarlarken bu izinlerin ne zaman istenmesi gerektiğini göz önünde bulundurun. İzinler önceden değil, gerektiğinde istenmelidir. Daha fazla bilgi için bkz . Android İzinleri Kılavuzu.

Nesne modeli

Aşağıdaki sınıflar ve arabirimler, Azure İletişim Hizmetleri Çağırma SDK'sının bazı önemli özelliklerini işler:

Veri Akışı Adı Açıklama
CallClient CallClient, Çağrı SDK'sının ana giriş noktasıdır.
TeamsCallAgent TeamsCallAgent, çağrıları başlatmak ve yönetmek için kullanılır.
TeamsCall TeamsCall Teams Çağrısını temsil etmek için kullanılan.
CommunicationTokenCredential , CommunicationTokenCredential örneğini TeamsCallAgentbaşlatmak için belirteç kimlik bilgisi olarak kullanılır.
CommunicationIdentifier CommunicationIdentifier, bir çağrının parçası olabilecek farklı bir katılımcı türü olarak kullanılır.

Kullanıcı erişim belirtecinden aracı oluşturma

Kullanıcı belirteci ile kimliği doğrulanmış bir çağrı aracısı örneği oluşturulabilir. Bu belirteç genellikle uygulamaya özgü kimlik doğrulamasına sahip bir hizmetten oluşturulur. Kullanıcı erişim belirteçleri hakkında daha fazla bilgi için Kullanıcı Erişim Belirteçleri kılavuzuna bakın.

Hızlı başlangıç için değerini Azure İletişim Hizmeti kaynağınız için oluşturulan bir kullanıcı erişim belirteci ile değiştirin <User_Access_Token> .


/**
 * 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();
    }
}

Arama aracısını kullanarak arama başlatma

Aramanın yerleştirilmesi, ekipler arama aracısı aracılığıyla yapılabilir ve yalnızca arayan kimliklerinin ve arama seçeneklerinin bir listesinin sağlanmasını gerektirir. Hızlı başlangıç için, video içermeyen varsayılan arama seçenekleri ve metin girişinden tek bir çağrı kimliği kullanılır.

/**
 * 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);
}

Aramayı Yanıtlama

Bir çağrıyı kabul etmek, yalnızca geçerli bağlama yönelik bir başvuru kullanılarak teams çağrı aracısı kullanılarak yapılabilir.

public void acceptACall(TeamsIncomingCall teamsIncomingCall){
	teamsIncomingCall.accept(this);
}

Teams Çağrısına Katılma

Kullanıcı, bir bağlantı geçirerek mevcut bir aramaya katılabilir.

/**
 * Join a call using a teams meeting link.
 */
public TeamsCall joinTeamsCall(TeamsCallAgent teamsCallAgent){
	TeamsMeetingLinkLocator link = new TeamsMeetingLinkLocator("meetingLink");
	TeamsCall call = teamsCallAgent.join(this, link);
}

Teams Çağrısına seçeneklerle katılma

Ayrıca sessize alma gibi önceden ayarlanmış seçeneklerle mevcut bir çağrıya da katılabiliriz.

/**
 * 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);
}

Gelen Arama Dinleyicisi Kurulumu

Gelen çağrıları ve bu kullanıcı tarafından yapılmayan diğer eylemleri algılayabilmek için dinleyicilerin ayarlanması gerekir.

private TeamsIncomingCall teamsincomingCall;
teamsCallAgent.addOnIncomingCallListener(this::handleIncomingCall);

private void handleIncomingCall(TeamsIncomingCall incomingCall) {
	this.teamsincomingCall = incomingCall;
}

Uygulamayı başlatın ve yankı botunu çağırın

Uygulama artık araç çubuğundaki "Uygulamayı Çalıştır" düğmesi kullanılarak başlatılabilir (Shift+F10). çağrısı 8:echo123yaparak aramalar yapabildiğinizi doğrulayın. Önceden kaydedilmiş bir ileti yürütülür ve ardından iletinizi size tekrar tekrarlayın.

Tamamlanmış uygulamayı gösteren ekran görüntüsü.

Uygulamanıza tek bir görüntülü arama eklemek için İletişim Hizmetleri çağrı SDK'sını kullanarak Azure İletişim Hizmetleri kullanmaya başlayın. Teams kimliğini kullanarak iOS için arama SDK'sını Azure İletişim Hizmetleri kullanarak görüntülü arama başlatmayı ve yanıtlamayı öğrenirsiniz.

Örnek Kod

Sonuna atlamak isterseniz bu hızlı başlangıcı GitHub'da örnek olarak indirebilirsiniz.

Önkoşullar

Ayarlama

Xcode projesi oluşturma

Xcode'da yeni bir iOS projesi oluşturun ve Tek Görünüm Uygulaması şablonunu seçin. Bu öğreticide SwiftUI çerçevesi kullanılır, bu nedenle Dili Swift olarak ve Kullanıcı Arabirimini SwiftUI olarak ayarlamanız gerekir. Bu hızlı başlangıç sırasında test oluşturmayacaksınız. Testleri Dahil Et seçeneğinin işaretini kaldırabilirsiniz.

Xcode içindeki Yeni Proje penceresini gösteren ekran görüntüsü.

CocoaPods'u yükleme

Mac bilgisayarınıza CocoaPods yüklemek için bu kılavuzu kullanın.

CocoaPods ile paketi ve bağımlılıkları yükleme

  1. Uygulamanız için bir Podfile oluşturmak için terminali açın ve proje klasörüne gidin ve pod init komutunu çalıştırın.

  2. öğesine aşağıdaki kodu Podfile ekleyin ve kaydedin. Bkz. SDK destek sürümleri.

platform :ios, '13.0'
use_frameworks!

target 'VideoCallingQuickstart' do
  pod 'AzureCommunicationCalling', '~> 2.10.0'
end
  1. Pod yüklemesini çalıştırın.

  2. Xcode ile dosyasını .xcworkspace açın.

Mikrofona ve kameraya erişim isteme

Cihazın mikrofon ve kamerasına erişmek için, uygulamanızın Bilgi Özellik Listesi'ni ve NSMicrophoneUsageDescription NSCameraUsageDescriptionile güncelleştirmeniz gerekir. İlişkili değeri, sistemin kullanıcıdan erişim istemek için kullandığı iletişim kutusunu içeren bir dizeye ayarlarsınız.

Proje ağacının girdisine Info.plist sağ tıklayın ve Kaynak Kodu Olarak > Aç'ı seçin. Aşağıdaki satırları en üst düzey <dict> bölüme ekleyin ve dosyayı kaydedin.

<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for video calling</string>

Uygulama çerçevesini ayarlama

Projenizin ContentView.swift dosyasını açın ve ve kitaplığını AVFoundationiçeri aktarmak için dosyanın en üstüne bir içeri aktarma AzureCommunicationCalling bildirimi ekleyin. AVFoundation, koddan ses izni yakalamak için kullanılır.

import AzureCommunicationCalling
import AVFoundation

Nesne modeli

Aşağıdaki sınıflar ve arabirimler, iOS için Azure İletişim Hizmetleri Çağırma SDK'sının bazı önemli özelliklerini işler.

Veri Akışı Adı Açıklama
CallClient CallClient, Çağrı SDK'sının ana giriş noktasıdır.
TeamsCallAgent TeamsCallAgent, çağrıları başlatmak ve yönetmek için kullanılır.
TeamsIncomingCall TeamsIncomingCall, gelen ekip çağrısını kabul etmek veya reddetmek için kullanılır.
CommunicationTokenCredential , CommunicationTokenCredential örneğini TeamsCallAgentbaşlatmak için belirteç kimlik bilgisi olarak kullanılır.
CommunicationIdentifier CommunicationIdentifier, kullanıcının kimliğini temsil etmek için kullanılır. Bu, aşağıdaki seçeneklerden biri olabilir: CommunicationUserIdentifier, PhoneNumberIdentifier veya CallingApplication.

Teams Arama Aracısı oluşturma

ContentView struct uygulamasını, kullanıcının çağrı başlatmasını ve sonlandırmasını sağlayan bazı basit kullanıcı arabirimi denetimleriyle değiştirin. Bu hızlı başlangıçta bu denetimlere iş mantığı ekleyeceğiz.

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()
    }
}

İstemcinin kimliğini doğrulama

Bir TeamsCallAgent örneği başlatmak için, çağrı yapmasını ve almasını sağlayan bir Kullanıcı Erişim Belirteci gerekir. Kullanılabilir bir belirteciniz yoksa kullanıcı erişim belirteci belgelerine bakın.

Belirteciniz olduktan sonra, içindeki geri ContentView.swiftçağırmaya onAppear aşağıdaki kodu ekleyin. değerini kaynağınız için geçerli bir kullanıcı erişim belirteci ile değiştirmeniz <USER ACCESS TOKEN> gerekir:

var userCredential: CommunicationTokenCredential?
do {
    userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
    print("ERROR: It was not possible to create user credential.")
    return
}

Teams CallAgent'ı başlatma ve Aygıt Yöneticisi erişme

bir öğesinden örnek oluşturmak TeamsCallAgent için, başlatıldıktan sonra zaman uyumsuz olarak bir TeamsCallAgent nesne döndüren yöntemini kullanıncallClient.createTeamsCallAgent.CallClient DeviceManager , ses/video akışlarını iletmek için bir çağrıda kullanılabilecek yerel cihazları listelemenize olanak tanır. Ayrıca bir kullanıcıdan mikrofona/kameraya erişim izni istemenizi sağlar.

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")
            }
        }
    }
}

İzin iste

Ses ve görüntü izinlerini istemek için geri çağırmaya aşağıdaki kodu onAppear eklememiz gerekir.

AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
    if granted {
        AVCaptureDevice.requestAccess(for: .video) { (videoGranted) in
            /* NO OPERATION */
        }
    }
}

Giden arama yerleştirme

yöntemi, startCall Aramayı Başlat düğmesine dokunulduğunda gerçekleştirilen eylem olarak ayarlanır. Bu hızlı başlangıçta, giden aramalar yalnızca varsayılan olarak seslidir. Görüntülü arama başlatmak için ile ayarlamamız VideoOptions LocalVideoStream ve aramanın ilk seçeneklerini ayarlamak için ile geçirmemiz startCallOptions gerekir.

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.
}

Teams toplantısına katılma

join yöntemi, kullanıcının teams toplantısına katılmasını sağlar.

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 ve RemotePariticipantObserver arama ortası olaylarını ve uzak katılımcıları yönetmek için kullanılır. İşlevdeki gözlemcileri ayarladık setTeamsCallAndObserver .

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")
    }
}

Gelen aramayı yanıtlama

Gelen aramayı yanıtlamak için, aramayı yanıtlamak veya reddetmek için gelen arama başlığını görüntülemek için bir TeamsIncomingCallHandler uygulayın. Aşağıdaki uygulamayı içine TeamsIncomingCallHandler.swiftyerleştirin.

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
        }
    }
}

içindeki geri ContentView.swiftçağırmaya aşağıdaki kodu onAppear ekleyerek örneğini TeamsIncomingCallHandler oluşturmamız gerekir:

Başarıyla oluşturulduktan sonra TeamsCallAgent için TeamsCallAgent bir temsilci ayarlayın:

self.teamsCallAgent!.delegate = incomingCallHandler

Gelen bir çağrı olduğunda işlevi TeamsIncomingCallHandler çağırarak showIncomingCallBanner ve decline düğmesini görüntüleranswer.

func showIncomingCallBanner(_ incomingCall: TeamsIncomingCall) {
    self.teamsIncomingCall = incomingCall
}

ve decline öğesine answer eklenen eylemler aşağıdaki kod olarak uygulanır. Aramayı görüntülü olarak yanıtlamak için yerel videoyu açmamız ve ile localVideoStreamseçeneklerini AcceptCallOptions ayarlamamız gerekir.

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.
    }
}

Olaylara Abone Olma

Çağrı sırasında değerler değiştiğinde bildirim almak üzere bir olay koleksiyonuna abone olmak için bir sınıf uygulayabiliriz TeamsCallObserver .

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
        }
    }
}

Kodu çalıştırma

Ürün > Çalıştır'ı seçerek veya (⌘-R) klavye kısayolunu kullanarak uygulamanızı iOS simülatöründe derleyebilir ve çalıştırabilirsiniz.

Kaynakları temizleme

İletişim Hizmetleri aboneliğini temizlemek ve kaldırmak istiyorsanız, kaynağı veya kaynak grubunu silebilirsiniz. Kaynak grubunun silinmesi, kaynak grubuyla ilişkili diğer tüm kaynakları da siler. Kaynakları temizleme hakkında daha fazla bilgi edinin.

Sonraki adımlar

Daha fazla bilgi için aşağıdaki makaleleri inceleyin: