Bien démarrer avec l’exemple d’appel

L'exemple de bannière d'appel de groupe Azure Communication Services illustre de quelle façon le SDK web Communication Services Calling peut être utilisé pour créer une expérience d'appel de groupe.

Dans cet exemple de démarrage rapide, nous apprenons comment l’exemple fonctionne avant de l’exécuter sur votre ordinateur local et de le déployer sur Azure en utilisant vos ressources Azure Communication Services.

Télécharger le code

Recherchez le projet correspondant à cet exemple sur GitHub. Une version de l’exemple qui comprend des fonctionnalités actuellement en préversion publique, telles que Teams Interop et l’enregistrement d’appels, est disponible sur une branche distincte.

Déployer sur Azure

Vue d’ensemble

L’exemple comporte à la fois une application côté client et une application côté serveur. L’application côté client est une application web React/Redux qui utilise le framework d’interface utilisateur Fluent de Microsoft. Cette application envoie des requêtes à une application côté serveur ASP.NET Core qui aide l’application côté client à se connecter à Azure.

Voici à quoi ressemble l’exemple :

Capture d’écran montrant la page d’arrivée de l’exemple d’application.

Lorsque vous appuyez sur le bouton « Start a call » (Démarrer un appel), l’application web récupère un jeton d’accès utilisateur à partir de l’application côté serveur. Ce jeton est ensuite utilisé pour connecter l’application cliente à Azure Communication Services. Une fois le jeton récupéré, le système vous invite à spécifier la caméra et le microphone que vous souhaitez utiliser. Vous pouvez désactiver/activer vos appareils avec des boutons bascule :

Capture d’écran montrant l’écran de pré-appel de l’exemple d’application.

Une fois que vous avez configuré votre nom d’affichage et vos appareils, vous pouvez rejoindre la session d’appel. Vous voyez à présent le canevas d’appel principal où se trouve le centre de l’expérience d’appel.

Capture d’écran montrant l’écran principal de l’exemple d’application.

Composants de l’écran principal d’appel :

  • Galerie multimédia : scène principale montrant les participants. Si une caméra est activée pour un participant, son flux vidéo est affiché ici. Chaque participant a une vignette individuelle qui comporte son nom d’affichage et son flux vidéo (le cas échéant).
  • En-tête: il s’agit de l’emplacement où se trouvent les principales commandes d’appel pour basculer entre les barres latérales de paramètres et de participants, activer/désactiver la vidéo et le micro, partager l’écran et quitter l’appel.
  • Barre latérale : c’est ici que les informations sur les participants et les paramètres sont affichées, en fonction de l’option choisie à l’aide des commandes de l’en-tête. Le composant peut être fermé à l’aide du « X » situé en haut à droite. La barre latérale des participants affiche une liste des participants et un lien pour inviter davantage d’utilisateurs à discuter. La barre latérale des paramètres vous permet de configurer les paramètres du microphone et de la caméra.

Consultez ci-dessous des informations supplémentaires sur les prérequis et les étapes à suivre pour configurer l’exemple.

Prérequis

Avant d’exécuter l’exemple pour la première fois

  1. Ouvrez une instance de PowerShell, de Terminal Windows, une invite de commandes ou équivalent, puis accédez au répertoire dans lequel vous souhaitez cloner l’exemple.

    git clone https://github.com/Azure-Samples/communication-services-web-calling-hero.git
    
  2. Obtenez Connection String à partir du portail Azure ou à l’aide d’Azure CLI.

    az communication list-key --name "<acsResourceName>" --resource-group "<resourceGroup>"
    

    Pour plus d’informations sur les chaînes de connexion, consultez Créer des ressources Azure Communication Services.

  3. Une fois que vous avez obtenu Connection String, ajoutez la chaîne de connexion au fichier samples/Server/appsetting.json. Entrez votre chaîne de connexion dans la variable : ResourceConnectionString.

  4. Obtenez Endpoint string à partir du portail Azure ou à l’aide d’Azure CLI.

    az communication list-key --name "<acsResourceName>" --resource-group "<resourceGroup>"
    

    Pour plus d’informations sur les chaînes de point de terminaison, consultez Créer des ressources Azure Communication Services

  5. Une fois que vous avez obtenu Endpoint String, ajoutez la chaîne de point de terminaison au fichier samples/Server/appsetting.json. Entrez votre chaîne de point de terminaison dans la variable EndpointUrl

Exécution locale

  1. Installer des dépendances

    npm run setup
    
  2. Démarrer l’application d’appel

    npm run start
    

    Cette opération ouvre un serveur client sur le port 3000 destinée à servir les fichiers du site web et un serveur d’API sur le port 8080 qui effectue des fonctionnalités, comme l’émission de jetons pour les participants aux appels.

Résolution des problèmes

  • L’application affiche un écran « Navigateur non pris en charge », alors que j’utilise un navigateur pris en charge.

    Si votre application est traitée sur un nom d’hôte autre que localhost, vous devez traiter le trafic via https et non http.

Publication dans Azure

  1. npm run setup
  2. npm run build
  3. npm run package
  4. Utiliser l’extension Azure et déployer le répertoire Appel/dist dans votre service d’application

Nettoyer les ressources

Si vous voulez nettoyer et supprimer un abonnement Communication Services, vous pouvez supprimer la ressource ou le groupe de ressources. La suppression du groupe de ressources efface également les autres ressources qui y sont associées. Apprenez-en davantage sur le nettoyage des ressources.

Étapes suivantes

Pour plus d’informations, consultez les articles suivants :

Documentation supplémentaire

  • Exemples : Recherchez d’autres exemples sur notre page Vue d’ensemble des exemples.
  • Redux - Gestion de l’état côté client
  • FluentUI - Bibliothèque d’interface utilisateur, Technologie Microsoft
  • React - Bibliothèque pour la création d’interfaces utilisateur
  • ASP.NET Core - Framework pour la création d’applications web

L'exemple de bannière d'appel de groupe pour iOS d'Azure Communication Services illustre de quelle façon le SDK iOS Communication Services Calling peut être utilisé pour créer une expérience d'appels vocaux et vidéo. Dans cet exemple de démarrage rapide, vous allez apprendre à configurer et à exécuter l’exemple. Une vue d’ensemble de l’exemple est fournie ci-après pour le contexte.

Télécharger le code

Recherchez le projet correspondant à cet exemple sur GitHub.

Vue d’ensemble

L'exemple est une application iOS native qui utilise les SDK iOS d'Azure Communication Services pour créer une expérience d'appels vocaux et vidéo. L'application se sert d'un composant côté serveur afin d'approvisionner des jetons d'accès qui sont ensuite utilisés pour initialiser le SDK d'Azure Communication Services. Pour configurer ce composant côté serveur, n’hésitez pas à suivre le tutoriel pour créer un service d’authentification approuvé avec Azure Functions.

Voici à quoi ressemble l’exemple :

Capture d’écran montrant la page d’arrivée de l’exemple d’application.

Quand vous appuyez sur le bouton « Démarrer un nouvel appel », l’application iOS vous invite à entrer votre nom d’affichage à utiliser pour l’appel.

Capture d’écran montrant l’écran de pré-appel de l’exemple d’application.

Après avoir appuyé sur « Suivant » dans l’écran « Démarrer un appel », vous avez la possibilité de partager l’ID de groupe de l’appel via la feuille de partage iOS.

Capture d’écran montrant l’écran avec l’ID de groupe de partage de l’exemple d’application.

L’application vous permet également de vous joindre à un appel Azure Communication Services existant en spécifiant l’ID de l’appel ou le lien d’ID Teams.

Capture d’écran montrant l’écran Se joindre à un appel de l’exemple d’application.

Après avoir rejoint un appel, vous êtes invité à autoriser l’application à accéder à votre caméra et à votre micro, si elle n’y est pas déjà autorisée. Gardes à l’esprit que, comme toutes les applications basées sur AVFoundation, les fonctionnalités audio et vidéo réelles sont disponibles seulement sur du matériel réel.

Une fois que vous avez configuré votre nom d’affichage et que vous avez rejoint l’appel, vous verrez le canevas d’appel principal où a lieu l’expérience d’appel principale.

Capture d’écran montrant l’écran principal de l’exemple d’application.

Composants de l’écran principal d’appel :

  • Galerie multimédia : scène principale montrant les participants. Si une caméra est activée pour un participant, son flux vidéo est affiché ici. Chaque participant a une vignette individuelle qui comporte son nom d’affichage et son flux vidéo (le cas échéant). La galerie prend en charge plusieurs participants et est mise à jour au fur et à mesure de l’ajout ou de la suppression de participants à l’appel.
  • Barre d’action : elle contient les principales commandes d’appel. Ces commandes vous permettent d’activer et de désactiver votre caméra et votre microphone, de partager votre écran et de quitter l’appel.

Vous trouverez ci-dessous des informations supplémentaires sur les prérequis et les étapes à suivre pour configurer l’exemple.

Prérequis

Exécution de l’exemple localement

L’exemple d’appel de groupe peut être exécuté localement en utilisant XCode. Les développeurs ont le choix d’utiliser leur appareil physique ou un émulateur pour tester l’application.

Avant d’exécuter l’exemple pour la première fois

  1. Installez les dépendances en exécutant pod install.
  2. Ouvrez AzureCalling.xcworkspace dans XCode.
  3. Créez un fichier texte à la racine, appelé AppSettings.xcconfig et définissez la valeur :
    communicationTokenFetchUrl = <your authentication endpoint, without the https:// component>
    

Exécuter l’exemple

Générez et exécutez l’exemple dans XCode, en utilisant la cible AzureCalling sur le simulateur ou l’appareil de votre choix.

(Facultatif) Sécurisation d’un point de terminaison d’authentification

À des fins de démonstration, cet exemple utilise par défaut un point de terminaison accessible publiquement pour extraire un jeton d'accès Azure Communication Services. Dans les scénarios de production, nous vous recommandons d’utiliser votre propre point de terminaison sécurisé pour provisionner vos propres jetons.

Avec une configuration supplémentaire, cet exemple prend en charge la connexion à un point de terminaison Microsoft Entra ID (Microsoft Entra ID) protégé, qui requiert une connexion de l'utilisateur pour que l'application puisse récupérer un jeton d'accès Azure Communication Services. Consultez les étapes ci-dessous :

  1. Activez l’authentification Microsoft Entra dans votre application.
  2. Accédez à la page de vue d’ensemble de l’application inscrite sous Inscriptions d’applications Microsoft Entra. Notez les valeurs de Application (client) ID, Directory (tenant) ID, Application ID URI

Configuration de Microsoft Entra dans le portail Azure.

  1. Créez un fichier AppSettings.xcconfig à la racine s’il n’est pas déjà présent et ajoutez les valeurs :
    communicationTokenFetchUrl = <Application ID URI, without the https:// component>
    aadClientId = <Application (client) ID>
    aadTenantId = <Directory (tenant) ID>
    

Nettoyer les ressources

Si vous voulez nettoyer et supprimer un abonnement Communication Services, vous pouvez supprimer la ressource ou le groupe de ressources. La suppression du groupe de ressources efface également les autres ressources qui y sont associées. Apprenez-en davantage sur le nettoyage des ressources.

Étapes suivantes

Pour plus d’informations, consultez les articles suivants :

Documentation supplémentaire

L'exemple de bannière d'appel de groupe pour Android d'Azure Communication Services illustre de quelle façon le SDK Android Communication Services Calling peut être utilisé pour créer une expérience d'appels vocaux et vidéo. Dans cet exemple de démarrage rapide, vous allez apprendre à configurer et à exécuter l’exemple. Une vue d’ensemble de l’exemple est fournie ci-après pour le contexte.

Télécharger le code

Recherchez le projet correspondant à cet exemple sur GitHub.

Vue d’ensemble

L’exemple est une application Android native qui utilise la bibliothèque de client Interface utilisateur Android d’Azure Communication Services pour créer une expérience avec des fonctionnalités d’appels vocaux et vidéo. L'application se sert d'un composant côté serveur afin d'approvisionner des jetons d'accès qui sont ensuite utilisés pour initialiser le SDK d'Azure Communication Services. Pour configurer ce composant côté serveur, n’hésitez pas à suivre le tutoriel pour créer un service d’authentification approuvé avec Azure Functions.

Voici à quoi ressemble l’exemple :

Capture d’écran montrant la page d’arrivée de l’exemple d’application.

Quand vous appuyez sur le bouton « Démarrer un nouvel appel », l’application Android vous invite à entrer votre nom d’affichage à utiliser pour l’appel.

Capture d’écran montrant l’écran de pré-appel de l’exemple d’application.

Après avoir appuyé sur « Suivant » dans la page « Démarrer un appel », vous avez la possibilité de partager l’« ID d’appel de groupe ».

Capture d’écran montrant l’écran Partager l’ID d’appel de groupe de l’exemple d’application.

L’application vous permet de vous joindre à un appel Azure Communication Services existant en spécifiant l’ID de l’appel ou le lien d’ID de réunion Teams et le nom d’affichage.

Capture d’écran montrant l’écran Se joindre à un appel de l’exemple d’application.

Après avoir rejoint un appel, vous êtes invité à autoriser l’application à accéder à votre caméra et à votre micro, si elle n’y est pas déjà autorisée. Vous voyez alors le canevas d’appel principal où réside l’expérience d’appel de base.

Capture d’écran montrant l’écran principal de l’exemple d’application.

Composants de l’écran principal d’appel :

  • Galerie multimédia : scène principale montrant les participants. Si une caméra est activée pour un participant, son flux vidéo est affiché ici. Chaque participant a une vignette individuelle qui comporte son nom d’affichage et son flux vidéo (le cas échéant). La galerie prend en charge plusieurs participants et est mise à jour au fur et à mesure de l’ajout ou de la suppression de participants à l’appel.
  • Barre d’action : elle contient les principales commandes d’appel. Ces commandes vous permettent d’activer et de désactiver votre caméra et votre microphone, de partager votre écran et de quitter l’appel.

Vous trouverez ci-dessous des informations supplémentaires sur les prérequis et les étapes à suivre pour configurer l’exemple.

Prérequis

Exécution de l’exemple localement

L'exemple d'appel de groupe peut être exécuté localement en utilisant Android Studio. Les développeurs ont le choix d’utiliser leur appareil physique ou un émulateur pour tester l’application.

Avant d’exécuter l’exemple pour la première fois

  1. Ouvrez Android Studio et sélectionnez Open an Existing Project.
  2. Ouvrez le dossier AzureCalling dans la version téléchargée de l'exemple.
  3. Développez Applications/ressources pour mettre à jour appSettings.properties. Définissez la valeur de la clé communicationTokenFetchUrl sur l'URL du point de terminaison d'authentification configuré comme condition préalable.

Exécuter l’exemple

Générez et exécutez l'exemple dans Android Studio.

(Facultatif) Sécurisation d’un point de terminaison d’authentification

À des fins de démonstration, cet exemple utilise par défaut un point de terminaison accessible publiquement pour extraire un jeton Azure Communication Services. Dans les scénarios de production, nous vous recommandons d’utiliser votre propre point de terminaison sécurisé pour provisionner vos propres jetons.

Avec une configuration supplémentaire, cet exemple prend en charge la connexion à un point de terminaison Microsoft Entra ID (Microsoft Entra ID) protégé, qui requiert une connexion de l'utilisateur pour que l'application puisse récupérer un jeton Azure Communication Services. Consultez les étapes ci-dessous :

  1. Activez l’authentification Microsoft Entra dans votre application.

  2. Accédez à la page de vue d’ensemble de l’application inscrite sous Inscriptions d’applications Microsoft Entra. Notez les valeurs de Package name, Signature hash, MSAL Configutaion.

Configuration de Microsoft Entra dans le portail Azure.

  1. Modifiez AzureCalling/app/src/main/res/raw/auth_config_single_account.json et définissez isAADAuthEnabled pour activer Microsoft Entra ID.

  2. Modifiez AndroidManifest.xml et définissez android:path sur le hachage de signature du magasin de clés. (Facultatif. La valeur actuelle utilise le hachage du magasins de clés debug.keystore fourni. Si un autre magasin de clés est utilisé, il doit être mis à jour.)

    <activity android:name="com.microsoft.identity.client.BrowserTabActivity">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <data
                     android:host="com.azure.samples.communication.calling"
                     android:path="/Signature hash" <!-- do not remove /. The current hash in AndroidManifest.xml is for debug.keystore. -->
                     android:scheme="msauth" />
             </intent-filter>
         </activity>
    
  3. Copiez la configuration de MSAL pour Android à partir du portail Azure et collez-la dans le fichier AzureCalling/app/src/main/res/raw/auth_config_single_account.json. Inclure "account_mode": "SINGLE"

       {
          "client_id": "",
          "authorization_user_agent": "DEFAULT",
          "redirect_uri": "",
          "account_mode" : "SINGLE",
          "authorities": [
             {
                "type": "AAD",
                "audience": {
                "type": "AzureADMyOrg",
                "tenant_id": ""
                }
             }
          ]
       }
    
  4. Modifiez AzureCalling/app/src/main/res/raw/auth_config_single_account.json et définissez la valeur de la clé communicationTokenFetchUrl comme l'URL de votre point de terminaison d'authentification sécurisé.

  5. Modifiez AzureCalling/app/src/main/res/raw/auth_config_single_account.json et définissez la valeur de la clé aadScopes à partir des étendues Azure Active Directory Expose an API.

  6. Définissez la valeur de graphURL dans AzureCalling/app/assets/appSettings.properties en tant que point de terminaison de l’API Graph pour extraire les informations utilisateur.

  7. Modifiez AzureCalling/app/src/main/assets/appSettings.properties et définissez la valeur de la clé tenant pour activer la connexion silencieuse afin que l’utilisateur n’ait pas à être authentifié à nouveau à chaque redémarrage de l’application.

Nettoyer les ressources

Si vous voulez nettoyer et supprimer un abonnement Communication Services, vous pouvez supprimer la ressource ou le groupe de ressources. La suppression du groupe de ressources efface également les autres ressources qui y sont associées. Apprenez-en davantage sur le nettoyage des ressources.

Étapes suivantes

Pour plus d’informations, consultez les articles suivants :

Documentation supplémentaire

L'exemple de bannière d'appel de groupe pour Windows de Azure Communication Services illustre de quelle façon le SDK Windows Communication Services Calling peut être utilisé pour créer une expérience d'appels vocaux et vidéo. Dans cet exemple, vous allez apprendre à configurer et à exécuter l’exemple. Une vue d’ensemble de l’exemple est fournie ci-après pour le contexte.

Ce démarrage rapide explique comment démarrer un appel vidéo à deux avec le kit de développement logiciel (SDK) Appel d’Azure Communication Services pour Windows.

Exemple de code UWP

Prérequis

Pour effectuer ce didacticiel, vous avez besoin de ce qui suit :

Configuration

Création du projet

Dans Visual Studio, créez un projet avec le modèle Application vide (Windows universel) pour configurer une application de plateforme Windows universelle (UWP) monopage.

Capture d’écran montrant la fenêtre Nouveau projet UWP dans Visual Studio.

Installer le package

Cliquez avec le bouton droit sur votre projet et accédez à Manage Nuget Packages pour installer la version Azure.Communication.Calling.WindowsClient 1.2.0-beta.1 ou supérieure. Vérifiez que l’option Inclure la préversion est activée.

Demander l'accès

Accédez à Package.appxmanifest, puis cliquez sur Capabilities. Cochez la case Internet (Client & Server) pour obtenir un accès entrant et sortant à Internet. Cochez la case Microphone pour accéder au flux audio du microphone. Cochez la case WebCam pour accéder à la caméra de l’appareil.

Ajoutez le code suivant à votre Package.appxmanifest en cliquant avec le bouton droit et en sélectionnant Afficher le code.

<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>RtmMvrUap.dll</Path>
<ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>

Configurer le framework d’application

Nous devons configurer une disposition de base pour attacher notre logique. Afin de passer un appel sortant, nous avons besoin d’une TextBox pour fournir l’ID d’utilisateur de l’appelé. Nous avons également besoin d’un bouton Start Call et d’un bouton Hang Up. En outre, nous avons besoin de voir un aperçu de la vidéo locale et d’afficher la vidéo à distance de l’autre participant. Nous avons donc besoin de deux éléments pour afficher les flux vidéo.

Ouvrez le fichier MainPage.xaml de votre projet et remplacez le contenu par l’implémentation suivante.

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

    <Grid x:Name="MainGrid" HorizontalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="200*"/>
            <RowDefinition Height="60*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" x:Name="AppTitleBar" Background="LightSeaGreen">
            <!-- Width of the padding columns is set in LayoutMetricsChanged handler. -->
            <!-- Using padding columns instead of Margin ensures that the background paints the area under the caption control buttons (for transparent buttons). -->
            <TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="4,4,0,0"/>
        </Grid>

        <TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" />

        <Grid Grid.Row="2" Background="LightGray">
            <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" Margin="10">
                <TextBlock VerticalAlignment="Center">Cameras:</TextBlock>
                <ComboBox x:Name="CameraList" HorizontalAlignment="Left" Grid.Column="0" DisplayMemberPath="Name" SelectionChanged="CameraList_SelectionChanged" Margin="10"/>
            </StackPanel>
            <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"/>
                <CheckBox x:Name="BackgroundBlur" Content="Background blur" Width="142" Margin="10,0,0,0" Click="BackgroundBlur_Click"/>
            </StackPanel>
        </StackPanel>
        <TextBox Grid.Row="4" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
    </Grid>
</Page>

Ouvrez App.xaml.cs (cliquez avec le bouton droit et sélectionnez Afficher le code) et ajoutez cette ligne en haut :

using CallingQuickstart;

Ouvrez MainPage.xaml.cs (cliquez avec le bouton droit et sélectionnez Afficher le code) et remplacez le contenu par l’implémentation suivante :

using Azure.Communication.Calling.WindowsClient;
using System;
using System.Collections.Generic;
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 = "<Azure Communication Services auth token>";
    
        private CallClient callClient;
        private CallTokenRefreshOptions callTokenRefreshOptions;
        private CallAgent callAgent;
        private CommunicationCall call = null;

        private LocalOutgoingAudioStream micStream;
        private LocalOutgoingVideoStream cameraStream;

        #region Page initialization
        public MainPage()
        {
            this.InitializeComponent();
            
            // Hide default title bar.
            var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
            coreTitleBar.ExtendViewIntoTitleBar = true;

            QuickstartTitle.Text = $"{Package.Current.DisplayName} - Ready";
            Window.Current.SetTitleBar(AppTitleBar);

            CallButton.IsEnabled = true;
            HangupButton.IsEnabled = !CallButton.IsEnabled;
            MuteLocal.IsChecked = MuteLocal.IsEnabled = !CallButton.IsEnabled;

            ApplicationView.PreferredLaunchViewSize = new Windows.Foundation.Size(800, 600);
            ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
        }

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            await InitCallAgentAndDeviceManagerAsync();

            base.OnNavigatedTo(e);
        }
#endregion

        private async Task InitCallAgentAndDeviceManagerAsync()
        {
            // Initialize call agent and Device Manager
        }

        private async void Agent_OnIncomingCallAsync(object sender, IncomingCall incomingCall)
        {
            // Accept an incoming call
        }

        private async void CallButton_Click(object sender, RoutedEventArgs e)
        {
            // Start a call with video
        }

        private async void HangupButton_Click(object sender, RoutedEventArgs e)
        {
            // End the current call
        }

        private async void Call_OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
        {
            var call = sender as CommunicationCall;

            if (call != null)
            {
                var state = call.State;

                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    QuickstartTitle.Text = $"{Package.Current.DisplayName} - {state.ToString()}";
                    Window.Current.SetTitleBar(AppTitleBar);

                    HangupButton.IsEnabled = state == CallState.Connected || state == CallState.Ringing;
                    CallButton.IsEnabled = !HangupButton.IsEnabled;
                    MuteLocal.IsEnabled = !CallButton.IsEnabled;
                });

                switch (state)
                {
                    case CallState.Connected:
                        {
                            break;
                        }
                    case CallState.Disconnected:
                        {
                            break;
                        }
                    default: break;
                }
            }
        }
        
        private async void CameraList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Handle camera selection
        }
    }
}

Modèle objet

Les classes et les interfaces suivantes gèrent certaines des principales fonctionnalités du SDK Azure Communication Services Calling :

Nom Description
CallClient CallClient est le point d’entrée principal de la bibliothèque d’appels de client.
CallAgent CallAgent sert à lancer et à joindre des appels.
CommunicationCall CommunicationCall sert à gérer les appels passés ou rejoints.
CallTokenCredential CallTokenCredential sert de jeton d’informations d'identification pour initier le CallAgent.
CommunicationUserIdentifier CommunicationUserIdentifier sert à représenter l’identité de l’utilisateur, parmi l’une des options suivantes : CommunicationUserIdentifier, PhoneNumberIdentifier ou CallingApplication.

Authentifier le client

Pour initialiser un CallAgent, il vous faut un jeton d’accès utilisateur. En règle général, ce jeton est généré à partir d’un service avec une authentification propre à l’application. Pour plus d’informations sur les jetons d’accès utilisateur, consultez le guide Jetons d’accès utilisateur.

Pour les besoins de ce guide de démarrage rapide, remplacez <AUTHENTICATION_TOKEN> par un jeton d’accès utilisateur généré pour votre ressource Azure Communication Services.

Une fois que vous disposez d’un jeton, initialisez une instance CallAgent avec le jeton qui permet d’établir et de recevoir des appels. Pour accéder aux caméras de l’appareil, il nous faut également une instance du Gestionnaire de périphériques.

Ajoutez le code suivant à la fonction InitCallAgentAndDeviceManagerAsync.

this.callClient = new CallClient(new CallClientOptions() {
    Diagnostics = new CallDiagnosticsOptions() { 
        AppName = "CallingQuickstart",
        AppVersion="1.0",
        Tags = new[] { "Calling", "ACS", "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();

CameraList.ItemsSource = deviceManager.Cameras.ToList();

if (camera != null)
{
    CameraList.SelectedIndex = 0;
}

callTokenRefreshOptions = new CallTokenRefreshOptions(false);
callTokenRefreshOptions.TokenRefreshRequested += OnTokenRefreshRequestedAsync;

var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);

var callAgentOptions = new CallAgentOptions()
{
    DisplayName = "Contoso",
    //https://github.com/lukes/ISO-3166-Countries-with-Regional-Codes/blob/master/all/all.csv
    EmergencyCallOptions = new EmergencyCallOptions() { CountryCode = "840" }
};


try
{
    this.callAgent = await this.callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
    //await this.callAgent.RegisterForPushNotificationAsync(await this.RegisterWNS());
    this.callAgent.CallsUpdated += OnCallsUpdatedAsync;
    this.callAgent.IncomingCallReceived += OnIncomingCallAsync;

}
catch(Exception ex)
{
    if (ex.HResult == -2147024809)
    {
        // E_INVALIDARG
        // Handle possible invalid token
    }
}

Démarrer un appel vidéo

Ajoutez l’implémentation au CallButton_Click pour démarrer un appel avec vidéo. nous devons énumérer les caméras avec l’instance du gestionnaire de périphériques et la construction LocalOutgoingVideoStream. Nous devons définir la valeur VideoOptions avec LocalVideoStream et la passer avec startCallOptions pour définir les options initiales de l’appel. En joignant LocalOutgoingVideoStream à un MediaElement, nous obtenons l’aperçu de la vidéo locale.

var callString = CalleeTextBox.Text.Trim();

if (!string.IsNullOrEmpty(callString))
{
    if (callString.StartsWith("8:")) // 1:1 Azure Communication Services call
    {
        call = await StartAcsCallAsync(callString);
    }
    else if (callString.StartsWith("+")) // 1:1 phone call
    {
        call = await StartPhoneCallAsync(callString, "+12133947338");
    }
    else if (Guid.TryParse(callString, out Guid groupId))// Join group call by group guid
    {
        call = await JoinGroupCallByIdAsync(groupId);
    }
    else if (Uri.TryCreate(callString, UriKind.Absolute, out Uri teamsMeetinglink)) //Teams meeting link
    {
        call = await JoinTeamsMeetingByLinkAsync(teamsMeetinglink);
    }
}

if (call != null)
{
    call.RemoteParticipantsUpdated += OnRemoteParticipantsUpdatedAsync;
    call.StateChanged += OnStateChangedAsync;
}

Ajoutez les méthodes pour démarrer ou rejoindre les différents types d’appel (appel Azure Communication Services 1:1, appel téléphonique 1:1, appel de groupe Azure Communication Services, participation à une réunion Teams, et ainsi de suite).

private async Task<CommunicationCall> StartAcsCallAsync(string acsCallee)
{
    var options = await GetStartCallOptionsAsynnc();
    var call = await this.callAgent.StartCallAsync( new [] { new UserCallIdentifier(acsCallee) }, options);
    return call;
}

private async Task<CommunicationCall> StartPhoneCallAsync(string acsCallee, string alternateCallerId)
{
    var options = await GetStartCallOptionsAsynnc();
    options.AlternateCallerId = new PhoneNumberCallIdentifier(alternateCallerId);

    var call = await this.callAgent.StartCallAsync( new [] { new PhoneNumberCallIdentifier(acsCallee) }, options);
    return call;
}

private async Task<CommunicationCall> JoinGroupCallByIdAsync(Guid groupId)
{
    var joinCallOptions = await GetJoinCallOptionsAsync();

    var groupCallLocator = new GroupCallLocator(groupId);
    var call = await this.callAgent.JoinAsync(groupCallLocator, joinCallOptions);
    return call;
}

private async Task<CommunicationCall> JoinTeamsMeetingByLinkAsync(Uri teamsCallLink)
{
    var joinCallOptions = await GetJoinCallOptionsAsync();

    var teamsMeetingLinkLocator = new TeamsMeetingLinkLocator(teamsCallLink.AbsoluteUri);
    var call = await callAgent.JoinAsync(teamsMeetingLinkLocator, joinCallOptions);
    return call;
}

private async Task<StartCallOptions> GetStartCallOptionsAsynnc()
{
    return new StartCallOptions() {
        OutgoingAudioOptions = new OutgoingAudioOptions() { IsOutgoingAudioMuted = true, OutgoingAudioStream = micStream  },
        OutgoingVideoOptions = new OutgoingVideoOptions() { OutgoingVideoStreams = new OutgoingVideoStream[] { cameraStream } }
    };
}

private async Task<JoinCallOptions> GetJoinCallOptionsAsync()
{
    return new JoinCallOptions() {
        OutgoingAudioOptions = new OutgoingAudioOptions() { IsOutgoingAudioMuted = true },
        OutgoingVideoOptions = new OutgoingVideoOptions() { OutgoingVideoStreams = new OutgoingVideoStream[] { cameraStream } }
    };
}

Ajoutez le code pour créer le LocalVideoStream en fonction de la caméra sélectionnée sur la méthode CameraList_SelectionChanged.

var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
cameraStream = new LocalOutgoingVideoStream(selectedCamerea);

 var localUri = await cameraStream.StartPreviewAsync();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
    LocalVideo.Source = MediaSource.CreateFromUri(localUri);
});

if (call != null)
{
    await call?.StartVideoAsync(cameraStream);
}

Acceptation d’un appel entrant

Ajoutez l’implémentation au OnIncomingCallAsync pour répondre à un appel entrant avec vidéo, passez-le LocalVideoStream à acceptCallOptions.

var incomingCall = args.IncomingCall;

var acceptCallOptions = new AcceptCallOptions() { 
    IncomingVideoOptions = new IncomingVideoOptions()
    {
        IncomingVideoStreamKind = VideoStreamKind.RemoteIncoming
    } 
};

_ = await incomingCall.AcceptAsync(acceptCallOptions);

Le participant distant et les flux vidéo distants

Tous les participants distants sont disponibles via la collection RemoteParticipants d’une instance d’appel. Une fois que l’appel devient connecté (CallState.Connected), nous pouvons accéder aux participants distants de l’appel et gérer les flux vidéo distants.

Remarque

Lorsqu’un utilisateur rejoint un appel, il peut accéder aux participants distants présents via la collection RemoteParticipants. L’événement RemoteParticipantsUpdated ne se déclenche pas pour ces participants existants. Cet événement se déclenche uniquement lorsqu’un participant distant rejoint ou quitte l’appel alors que l’utilisateur est déjà présent dans l’appel.


private async void Call_OnVideoStreamsUpdatedAsync(object sender, RemoteVideoStreamsEventArgs args)
{
    foreach (var remoteVideoStream in args.AddedRemoteVideoStreams)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
        {
            RemoteVideo.Source = await remoteVideoStream.Start();
        });
    }

    foreach (var remoteVideoStream in args.RemovedRemoteVideoStreams)
    {
        remoteVideoStream.Stop();
    }
}

private async void Agent_OnCallsUpdatedAsync(object sender, CallsUpdatedEventArgs args)
{
    var removedParticipants = new List<RemoteParticipant>();
    var addedParticipants = new List<RemoteParticipant>();

    foreach(var call in args.RemovedCalls)
    {
        removedParticipants.AddRange(call.RemoteParticipants.ToList<RemoteParticipant>());
    }

    foreach (var call in args.AddedCalls)
    {
        addedParticipants.AddRange(call.RemoteParticipants.ToList<RemoteParticipant>());
    }

    await OnParticipantChangedAsync(removedParticipants, addedParticipants);
}

private async Task OnParticipantChangedAsync(IEnumerable<RemoteParticipant> removedParticipants, IEnumerable<RemoteParticipant> addedParticipants)
{
    foreach (var participant in removedParticipants)
    {
        foreach(var incomingVideoStream in  participant.IncomingVideoStreams)
        {
            var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
            if (remoteVideoStream != null)
            {
                await remoteVideoStream.StopPreviewAsync();
            }
        }
        participant.VideoStreamStateChanged -= OnVideoStreamStateChanged;
    }

    foreach (var participant in addedParticipants)
    {
        participant.VideoStreamStateChanged += OnVideoStreamStateChanged;
    }
}

private void OnVideoStreamStateChanged(object sender, VideoStreamStateChangedEventArgs e)
{
    CallVideoStream callVideoStream = e.CallVideoStream;

    switch (callVideoStream.StreamDirection)
    {
        case StreamDirection.Outgoing:
            OnOutgoingVideoStreamStateChanged(callVideoStream as OutgoingVideoStream);
            break;
        case StreamDirection.Incoming:
            OnIncomingVideoStreamStateChanged(callVideoStream as IncomingVideoStream);
            break;
    }
}

private async void OnIncomingVideoStreamStateChanged(IncomingVideoStream incomingVideoStream)
{
    switch (incomingVideoStream.State)
    {
        case VideoStreamState.Available:
        {
            switch (incomingVideoStream.Kind)
            {
                case VideoStreamKind.RemoteIncoming:
                    var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
                    var uri = await remoteVideoStream.StartPreviewAsync();

                    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        RemoteVideo.Source = MediaSource.CreateFromUri(uri);
                    });
                    break;

                case VideoStreamKind.RawIncoming:
                    break;
            }
            break;
        }
        case VideoStreamState.Started:
            break;
        case VideoStreamState.Stopping:
            break;
        case VideoStreamState.Stopped:
            if (incomingVideoStream.Kind == VideoStreamKind.RemoteIncoming)
            {
                var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
                await remoteVideoStream.StopPreviewAsync();
            }
            break;
        case VideoStreamState.NotAvailable:
            break;
    }

}

Afficher des vidéos distantes

Attachez chaque flux de vidéo à distance au MediaElement.

private async Task AddVideoStreamsAsync(IReadOnlyList<RemoteVideoStream> remoteVideoStreams)
{
    foreach (var remoteVideoStream in remoteVideoStreams)
    {
        var remoteUri = await remoteVideoStream.Start();

        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            RemoteVideo.Source = remoteUri;
            RemoteVideo.Play();
        });
    }
}

Mise à jour de l’état d’appel

Nous devons nettoyer les convertisseurs vidéo une fois l’appel déconnecté, et gérer le cas lorsque les participants distants rejoignent initialement l’appel.

private async void Call_OnStateChanged(object sender, PropertyChangedEventArgs args)
{
    switch (((Call)sender).State)
    {
        case CallState.Disconnected:
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                LocalVideo.Source = null;
                RemoteVideo.Source = null;
            });
            break;

        case CallState.Connected:
            foreach (var remoteParticipant in call.RemoteParticipants)
            {
                String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
                remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
                await AddVideoStreams(remoteParticipant.VideoStreams);
                remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdated;
            }
            break;

        default:
            break;
    }
}

Terminer un appel

Terminez l’appel en cours quand l’utilisateur clique sur le bouton Hang Up. Ajoutez l’implémentation à HangupButton_Click pour mettre fin à un appel avec le callAgent que nous avons créé et supprimez la mise à jour du participant et les gestionnaires d’événements d’état d’appel.

var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
    try
    {
        await call.HangUpAsync(new HangUpOptions() { ForEveryone = true });
    }
    catch(Exception ex) 
    {
    }
}

Exécuter le code

Vous pouvez générer et exécuter le code sur Visual Studio. Les plateformes de solution que nous prenons en charge sont ARM64, x64 et x86.

Vous pouvez passer un appel vidéo sortant en entrant un ID d’utilisateur dans le champ de texte, puis en cliquant sur le bouton Start Call.

Remarque : l’appel de 8:echo123 arrête la diffusion vidéo, car le bot d’écho ne prend pas en charge la diffusion vidéo.

Pour plus d’informations sur les identifiants utilisateur, consultez le guide Jetons d’accès utilisateur.

Exemple de code WinUI 3

Prérequis

Pour effectuer ce didacticiel, vous avez besoin de ce qui suit :

Configuration

Création du projet

Dans Visual Studio, créez un projet avec le modèle Application vide, Empaqueté (WinUI 3 dans Bureau) pour configurer une application WinUI 3 monopage.

Capture d’écran montrant la fenêtre Nouveau projet WinUI dans Visual Studio.

Installer le package

Cliquez avec le bouton droit sur votre projet, puis accédez à Manage Nuget Packages pour installer la version Azure.Communication.Calling.WindowsClient 1.0.0 ou supérieure. Vérifiez que l’option Inclure la préversion est activée.

Demander l'accès

Capture d’écran montrant la demande d’accès à Internet et au microphone dans Visual Studio.

Ajoutez le code suivant à votre fichier app.manifest :

<file name="RtmMvrMf.dll">
    <activatableClass name="VideoN.VideoSchemeHandler" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1" />
</file>

Configurer le framework d’application

Nous devons configurer une disposition de base pour attacher notre logique. Afin de passer un appel sortant, nous avons besoin d’une TextBox pour fournir l’ID d’utilisateur de l’appelé. Nous avons également besoin d’un bouton Start Call et d’un bouton Hang Up. En outre, nous avons besoin de voir un aperçu de la vidéo locale et d’afficher la vidéo à distance de l’autre participant. Nous avons donc besoin de deux éléments pour afficher les flux vidéo.

Ouvrez le fichier MainWindow.xaml de votre projet et remplacez le contenu par l’implémentation suivante.

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

    <Grid x:Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="32"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="200*"/>
            <RowDefinition Height="60*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" x:Name="AppTitleBar" Background="LightSeaGreen">
            <!-- Width of the padding columns is set in LayoutMetricsChanged handler. -->
            <!-- Using padding columns instead of Margin ensures that the background paints the area under the caption control buttons (for transparent buttons). -->
            <TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="4,4,0,0"/>
        </Grid>

        <TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" />

        <Grid Grid.Row="2" Background="LightGray">
            <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" Margin="10">
                <TextBlock VerticalAlignment="Center">Cameras:</TextBlock>
                <ComboBox x:Name="CameraList" HorizontalAlignment="Left" Grid.Column="0" DisplayMemberPath="Name" SelectionChanged="CameraList_SelectionChanged" Margin="10"/>
            </StackPanel>
            <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"/>
                <CheckBox x:Name="BackgroundBlur" Content="Background blur" Width="142" Margin="10,0,0,0" Click="BackgroundBlur_Click"/>
            </StackPanel>
        </StackPanel>
        <TextBox Grid.Row="4" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
    </Grid>    
</Page>

Ouvrez App.xaml.cs (cliquez avec le bouton droit et sélectionnez Afficher le code) et ajoutez cette ligne en haut :

using CallingQuickstart;

Ouvrez MainWindow.xaml.cs (cliquez avec le bouton droit et sélectionnez Afficher le code) et remplacez le contenu par l’implémentation suivante :

using Azure.Communication.Calling.WindowsClient;
using Azure.WinRT.Communication;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Media.Core;

namespace CallingQuickstart
{
    public sealed partial class MainWindow : Window
    {
        CallAgent callAgent;
        Call call;
        DeviceManager deviceManager;
        Dictionary<string, RemoteParticipant> remoteParticipantDictionary = new Dictionary<string, RemoteParticipant>();

        public MainWindow()
        {
            this.InitializeComponent();
            Task.Run(() => this.InitCallAgentAndDeviceManagerAsync()).Wait();
        }

        private async Task InitCallAgentAndDeviceManagerAsync()
        {
            // Initialize call agent and Device Manager
        }

        private async void Agent_OnIncomingCallAsync(object sender, IncomingCall incomingCall)
        {
            // Accept an incoming call
        }

        private async void CallButton_Click(object sender, RoutedEventArgs e)
        {
            // Start a call with video
        }

        private async void HangupButton_Click(object sender, RoutedEventArgs e)
        {
            // End the current call
        }

        private async void Call_OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
        {
            var state = (sender as Call)?.State;
            this.DispatcherQueue.TryEnqueue(() => {
                State.Text = state.ToString();
            });
        }
    }
}

Modèle objet

Les classes et les interfaces suivantes gèrent certaines des principales fonctionnalités du SDK Azure Communication Services Calling :

Nom Description
CallClient CallClient est le point d’entrée principal de la bibliothèque d’appels de client.
CallAgent CallAgent sert à lancer et à joindre des appels.
CommunicationCall CommunicationCall sert à gérer les appels passés ou rejoints.
CallTokenCredential CallTokenCredential sert de jeton d’informations d'identification pour initier le CallAgent.
CommunicationUserIdentifier CommunicationUserIdentifier sert à représenter l’identité de l’utilisateur, parmi l’une des options suivantes : CommunicationUserIdentifier, PhoneNumberIdentifier ou CallingApplication.

Authentifier le client

Pour initialiser un CallAgent, il vous faut un jeton d’accès utilisateur. En règle général, ce jeton est généré à partir d’un service avec une authentification propre à l’application. Pour plus d’informations sur les jetons d’accès utilisateur, consultez le guide Jetons d’accès utilisateur.

Pour les besoins de ce guide de démarrage rapide, remplacez <AUTHENTICATION_TOKEN> par un jeton d’accès utilisateur généré pour votre ressource Azure Communication Services.

Une fois que vous disposez d’un jeton, initialisez une instance CallAgent avec le jeton qui permet d’établir et de recevoir des appels. Pour accéder aux caméras de l’appareil, il nous faut également une instance du Gestionnaire de périphériques.

Ajoutez le code suivant à la fonction InitCallAgentAndDeviceManagerAsync.

var callClient = new CallClient();
this.deviceManager = await callClient.GetDeviceManagerAsync();

var tokenCredential = new CallTokenCredential("<AUTHENTICATION_TOKEN>");
var callAgentOptions = new CallAgentOptions()
{
    DisplayName = "<DISPLAY_NAME>"
};

this.callAgent = await callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
this.callAgent.OnCallsUpdated += Agent_OnCallsUpdatedAsync;
this.callAgent.OnIncomingCall += Agent_OnIncomingCallAsync;

Démarrer un appel vidéo

Ajoutez l’implémentation au CallButton_Click pour démarrer un appel avec vidéo. nous devons énumérer les caméras avec l’instance du gestionnaire de périphériques et la construction LocalVideoStream. Nous devons définir la valeur VideoOptions avec LocalVideoStream et la passer avec startCallOptions pour définir les options initiales de l’appel. En joignant LocalVideoStream à un MediaPlayerElement, nous obtenons l’aperçu de la vidéo locale.

var startCallOptions = new StartCallOptions();

if (this.deviceManager.Cameras?.Count > 0)
{
    var videoDeviceInfo = this.deviceManager.Cameras?.FirstOrDefault();
    if (videoDeviceInfo != null)
    {
        var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
        cameraStream = new LocalOutgoingVideoStream(selectedCamerea);

        var localUri = await cameraStream.StartPreviewAsync();
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            LocalVideo.Source = MediaSource.CreateFromUri(localUri);
        });

        startCallOptions.VideoOptions = new OutgoingVideoOptions(new[] { cameraStream });
    }
}

var callees = new ICommunicationIdentifier[1]
{
    new CommunicationUserIdentifier(CalleeTextBox.Text.Trim())
};

this.call = await this.callAgent.StartCallAsync(callees, startCallOptions);
this.call.OnRemoteParticipantsUpdated += Call_OnRemoteParticipantsUpdatedAsync;
this.call.OnStateChanged += Call_OnStateChangedAsync;

Acceptation d’un appel entrant

Ajoutez l’implémentation au Agent_OnIncomingCallAsync pour répondre à un appel entrant avec vidéo, passez-le LocalVideoStream à acceptCallOptions.

var acceptCallOptions = new AcceptCallOptions();

if (this.deviceManager.Cameras?.Count > 0)
{
    var videoDeviceInfo = this.deviceManager.Cameras?.FirstOrDefault();
    if (videoDeviceInfo != null)
    {
        var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
        cameraStream = new LocalOutgoingVideoStream(selectedCamerea);

        var localUri = await cameraStream.StartPreviewAsync();
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            LocalVideo.Source = MediaSource.CreateFromUri(localUri);
        });

        acceptCallOptions.VideoOptions = new OutgoingVideoOptions(new[] { localVideoStream });
    }
}

call = await incomingCall.AcceptAsync(acceptCallOptions);

Le participant distant et les flux vidéo distants

Tous les participants distants sont disponibles via la collection RemoteParticipants d’une instance d’appel. Une fois l’appel connecté, nous pouvons accéder aux participants distants de l’appel et gérer les flux de vidéo à distance.

Remarque

Lorsqu’un utilisateur rejoint un appel, il peut accéder aux participants distants présents via la collection RemoteParticipants. L’événement OnRemoteParticipantsUpdated ne se déclenche pas pour ces participants existants. Cet événement se déclenche uniquement lorsqu’un participant distant rejoint ou quitte l’appel alors que l’utilisateur est déjà présent dans l’appel.

private async void Call_OnVideoStreamsUpdatedAsync(object sender, RemoteVideoStreamsEventArgs args)
{
    foreach (var remoteVideoStream in args.AddedRemoteVideoStreams)
    {
        this.DispatcherQueue.TryEnqueue(async () => {
            RemoteVideo.Source = MediaSource.CreateFromUri(await remoteVideoStream.Start());
            RemoteVideo.MediaPlayer.Play();
        });
    }

    foreach (var remoteVideoStream in args.RemovedRemoteVideoStreams)
    {
        remoteVideoStream.Stop();
    }
}

private async void Agent_OnCallsUpdatedAsync(object sender, CallsUpdatedEventArgs args)
{
    foreach (var call in args.AddedCalls)
    {
        foreach (var remoteParticipant in call.RemoteParticipants)
        {
            var remoteParticipantMRI = remoteParticipant.Identifier.ToString();
            this.remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
            await AddVideoStreamsAsync(remoteParticipant.VideoStreams);
            remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdatedAsync;
        }
    }
}

private async void Call_OnRemoteParticipantsUpdatedAsync(object sender, ParticipantsUpdatedEventArgs args)
{
    foreach (var remoteParticipant in args.AddedParticipants)
    {
        String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
        this.remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
        await AddVideoStreamsAsync(remoteParticipant.VideoStreams);
        remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdatedAsync;
    }

    foreach (var remoteParticipant in args.RemovedParticipants)
    {
        String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
        this.remoteParticipantDictionary.Remove(remoteParticipantMRI);
    }
}

Afficher des vidéos distantes

Attachez chaque flux de vidéo à distance au MediaPlayerElement.

private async Task AddVideoStreamsAsync(IReadOnlyList<RemoteVideoStream> remoteVideoStreams)
{
    foreach (var remoteVideoStream in remoteVideoStreams)
    {
        var remoteUri = await remoteVideoStream.Start();

        this.DispatcherQueue.TryEnqueue(() => {
            RemoteVideo.Source = MediaSource.CreateFromUri(remoteUri);
            RemoteVideo.MediaPlayer.Play();
        });
    }
}

Mise à jour de l’état d’appel

Nous devons nettoyer les convertisseurs vidéo une fois l’appel déconnecté, et gérer le cas lorsque les participants distants rejoignent initialement l’appel.

private async void Call_OnStateChanged(object sender, PropertyChangedEventArgs args)
{
    switch (((Call)sender).State)
    {
        case CallState.Disconnected:
            this.DispatcherQueue.TryEnqueue(() => { =>
            {
                LocalVideo.Source = null;
                RemoteVideo.Source = null;
            });
            break;

        case CallState.Connected:
            foreach (var remoteParticipant in call.RemoteParticipants)
            {
                String remoteParticipantMRI = remoteParticipant.Identifier.ToString();
                remoteParticipantDictionary.TryAdd(remoteParticipantMRI, remoteParticipant);
                await AddVideoStreams(remoteParticipant.VideoStreams);
                remoteParticipant.OnVideoStreamsUpdated += Call_OnVideoStreamsUpdated;
            }
            break;

        default:
            break;
    }
}

Terminer un appel

Terminez l’appel en cours quand l’utilisateur clique sur le bouton Hang Up. Ajoutez l’implémentation à HangupButton_Click pour mettre fin à un appel avec le callAgent que nous avons créé et supprimez la mise à jour du participant et les gestionnaires d’événements d’état d’appel.

this.call.OnRemoteParticipantsUpdated -= Call_OnRemoteParticipantsUpdatedAsync;
this.call.OnStateChanged -= Call_OnStateChangedAsync;
await this.call.HangUpAsync(new HangUpOptions());

Exécuter le code

Vous pouvez générer et exécuter le code sur Visual Studio. Les plateformes de solution que nous prenons en charge sont ARM64, x64 et x86.

Vous pouvez passer un appel vidéo sortant en entrant un ID d’utilisateur dans le champ de texte, puis en cliquant sur le bouton Start Call.

Remarque : l’appel de 8:echo123 arrête la diffusion vidéo, car le bot d’écho ne prend pas en charge la diffusion vidéo.

Pour plus d’informations sur les identifiants utilisateur, consultez le guide Jetons d’accès utilisateur.