Introducción a la muestra de elementos principales de una llamada

En el ejemplo de elementos principales de llamada grupal de Azure Communication Services se muestra cómo se puede usar el SDK web de llamadas de Communication Services para crear una experiencia de llamada grupal.

En esta guía de inicio rápido con un ejemplo, se muestra cómo funciona el ejemplo antes de ejecutarlo en su equipo local y, después, lo implementa en Azure con sus propios recursos de Azure Communication Services.

Descarga de código

Busque el proyecto de este ejemplo en GitHub. En otra rama, hay una versión del ejemplo que incluye características que actualmente están en versión preliminar pública, como la interoperabilidad entre equipos y la grabación de llamadas.

Implementación en Azure

Información general

El ejemplo tiene una aplicación del lado cliente y una aplicación del lado servidor. La aplicación del lado cliente es una aplicación web React/Redux que usa el marco de interfaz de usuario Fluent de Microsoft. Esta aplicación envía solicitudes a una aplicación del lado servidor de ASP.NET Core que ayuda a la aplicación del lado cliente a conectarse a Azure.

El ejemplo tendrá una apariencia similar a la siguiente:

Captura de pantalla que muestra la página de aterrizaje de la aplicación de ejemplo.

Cuando se presiona el botón "Start a call", la aplicación web captura un token de acceso de usuario de la aplicación del lado servidor. A continuación, este token se usa para conectar la aplicación cliente con Azure Communication Services. Una vez recuperado el token, se le pide que especifique la cámara y el micrófono que quiere usar. Puede deshabilitar o habilitar los dispositivos con los controles de alternancia:

Captura de pantalla que muestra la pantalla anterior a la llamada en la aplicación de ejemplo.

Una vez que configure el nombre para mostrar y los dispositivos, puede unirse a la sesión de llamada. Verá el lienzo de llamada principal en el que se encuentra la experiencia de llamada principal.

Captura de pantalla que muestra la pantalla principal de la aplicación de ejemplo.

Componentes de la pantalla principal de llamada:

  • Galería multimedia: la fase principal en la que se muestran los participantes. Si un participante tiene habilitada la cámara, aquí se muestra su fuente de vídeo. Cada participante tiene un icono individual que muestra su nombre para mostrar y la transmisión de vídeo (si hay alguna).
  • Encabezado: aquí es donde se encuentran los controles de llamada principales para ajustar la configuración y la barra lateral de participantes, activar o desactivar el vídeo y mezclas, compartir la pantalla y abandonar la llamada.
  • Barra lateral: aquí es donde se muestran los participantes y la información de configuración cuando se ajustan con los controles del encabezado. El componente se puede descartar con la "X" de la esquina superior derecha. En la barra lateral de participantes se muestra una lista de participantes y un vínculo para invitar a más usuarios a chatear. La barra lateral de configuración permite configurar las opciones del micrófono y la cámara.

A continuación se proporciona más información sobre los requisitos previos y los pasos para configurar el ejemplo.

Requisitos previos

Antes de ejecutar el ejemplo por primera vez

  1. Abra una instancia de PowerShell, Terminal Windows, símbolo del sistema o equivalente y navegue hasta el directorio donde le gustaría clonar el ejemplo.

    git clone https://github.com/Azure-Samples/communication-services-web-calling-hero.git
    
  2. Obtenga Connection String en Azure Portal o mediante la CLI de Azure.

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

    Para obtener más información sobre las cadenas de conexión, consulte Creación de un recurso de Azure Communication Services.

  3. Cuando obtenga el valor de Connection String, agréguelo al archivo samples/Server/appsetting.json. Escriba la cadena de conexión en la variable: ResourceConnectionString.

  4. Obtenga Endpoint string en Azure Portal o mediante la CLI de Azure.

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

    Para obtener más información sobre las cadenas de punto de conexión, consulte el artículo Creación de recursos de Azure Communication Services.

  5. Cuando obtenga el valor de Endpoint String, agréguelo al archivo samples/Server/appsetting.json. Escriba la cadena de punto de conexión en la variable EndpointUrl

Ejecución local

  1. Instalar dependencias

    npm run setup
    
  2. Inicio de la aplicación de llamada

    npm run start
    

    Esto abre una conexión cliente-servidor en el puerto 3000 que sirve los archivos del sitio web, así como un servidor de API en el puerto 8080 que realiza funciones como la creación de tokens para los participantes de las llamadas.

Solucionar problemas

  • La aplicación muestra una pantalla con un mensaje de "Explorador no compatible", pero estoy usando un explorador compatible.

    Si la aplicación está dando servicio mediante un nombre de host distinto de localhost, deberá ofrecer el tráfico a través del protocolo https y no mediante http.

Publicar en Azure

  1. npm run setup
  2. npm run build
  3. npm run package
  4. Use la extensión de Azure e implemente el directorio Calling/dist en el servicio de aplicaciones

Limpieza de recursos

Si quiere limpiar y quitar una suscripción a Communication Services, puede eliminar el recurso o grupo de recursos. Al eliminar el grupo de recursos, también se elimina cualquier otro recurso que esté asociado a él. Obtenga más información sobre la limpieza de recursos.

Pasos siguientes

Para más información, consulte los siguientes artículos.

Lecturas adicionales

  • Ejemplos: encuentre más ejemplos en nuestra página de información general de ejemplos.
  • Redux: Administración de estado del lado cliente
  • FluentUI: Biblioteca de interfaz de usuario con tecnología de Microsoft
  • React: Biblioteca para compilar interfaces de usuario
  • ASP.NET Core: Marco para compilar aplicaciones web

El ejemplo de elementos principales de llamada de grupo para iOS de Azure Communication Services muestra cómo usar Calling SDK de Communication Services para iOS para crear una experiencia de llamada de grupo que incluya voz y vídeo. En este inicio rápido de ejemplo, aprenderá a configurar y ejecutar el ejemplo. Se proporciona información general del ejemplo con fines de contextualización.

Descarga de código

Busque el proyecto de este ejemplo en GitHub.

Información general

El ejemplo consiste en una aplicación iOS nativa que usa los SDK de Azure Communication Services para iOS para crear una experiencia de llamada que incluya llamadas de voz y vídeo. La aplicación usa un componente de servidor para aprovisionar los tokens de acceso que, posteriormente, se usan para inicializar el SDK de Azure Communication Services. Para configurar este componente de servidor, siga el tutorial Creación de un servicio de autenticación de confianza mediante Azure Functions.

El ejemplo tendrá una apariencia similar a la siguiente:

Captura de pantalla que muestra la página de aterrizaje de la aplicación de ejemplo.

Al presionar el botón "Iniciar nueva llamada", la aplicación de iOS le pide que escriba el nombre para mostrar que se usará para la llamada.

Captura de pantalla que muestra la pantalla anterior a la llamada en la aplicación de ejemplo.

Después de pulsar "Siguiente" en la pantalla "Iniciar llamada", podrá compartir el identificador de grupo de la llamada a través de la hoja de recursos compartidos de iOS.

Captura de pantalla que muestra la pantalla para compartir el Id. de grupo para la aplicación de ejemplo.

La aplicación también le permite unirse a una llamada existente de Azure Communication Services mediante el identificador de esa llamada o el vínculo de identificador de Teams.

Captura de pantalla que muestra la pantalla para unirse a una llamada en la aplicación de ejemplo.

Después de unirse a una llamada, se le solicitará que conceda permiso a la aplicación para acceder a la cámara y el micrófono si aún no están autorizados. Tenga en cuenta que, al igual que todas las aplicaciones basadas en AVFoundation, la verdadera funcionalidad de audio y vídeo solo está disponible en hardware real.

Después de configurar el nombre para mostrar y unirse a la llamada, verá el lienzo de llamadas principal donde reside la experiencia de llamada principal.

Captura de pantalla que muestra la pantalla principal de la aplicación de ejemplo.

Componentes de la pantalla principal de llamada:

  • Galería multimedia: la fase principal en la que se muestran los participantes. Si un participante tiene habilitada la cámara, aquí se muestra su fuente de vídeo. Cada participante tiene un icono individual con su nombre para mostrar y la secuencia de vídeo (si la hay). La galería admite varios participantes y se actualiza al agregarlos o al quitarlos de la llamada.
  • Barra de acciones: Aquí es donde se encuentran los principales controles de llamada. Estos controles permiten activar o desactivar el vídeo y el micrófono, compartir la pantalla y abandonar la llamada.

A continuación encontrará más información sobre los requisitos previos y los pasos para configurar el ejemplo.

Requisitos previos

Ejecución local del ejemplo

El ejemplo de llamada de grupo se puede ejecutar localmente mediante XCode. Los desarrolladores pueden usar su dispositivo físico o un emulador para probar la aplicación.

Antes de ejecutar el ejemplo por primera vez

  1. Instale las dependencias mediante la ejecución de pod install.
  2. Abra AzureCalling.xcworkspace en XCode.
  3. Cree un archivo de texto en la raíz, denominado AppSettings.xcconfig y establezca el valor:
    communicationTokenFetchUrl = <your authentication endpoint, without the https:// component>
    

Ejecución del ejemplo

Compile y ejecute el ejemplo en XCode, utilizando el destino AzureCalling en el simulador o el dispositivo que prefiera.

(Opcional) Protección de un punto de conexión de autenticación

Con fines de demostración, en este ejemplo se usa de forma predeterminada un punto de conexión de acceso público para capturar un token de acceso de Azure Communication Services. En escenarios de producción, se recomienda usar un punto de conexión propio protegido para aprovisionar los tokens.

Mediante alguna configuración adicional, este ejemplo permite conectarse a un punto de conexión protegido de Microsoft Entra ID (Microsoft Entra ID) de manera que se solicite el inicio de sesión de usuario para que la aplicación capture un token de acceso de Azure Communication Services. Consulte los siguientes pasos:

  1. Habilite la autenticación de Microsoft Entra en la aplicación.
  2. Vaya a la página de información general de la aplicación registrada en Registros de aplicaciones de Microsoft Entra. Anote los valores de Application (client) ID, Directory (tenant) ID, Application ID URI.

Configuración de Microsoft Entra en Azure Portal.

  1. Cree un archivo AppSettings.xcconfig en la raíz si aún no está presente y agregue los valores:
    communicationTokenFetchUrl = <Application ID URI, without the https:// component>
    aadClientId = <Application (client) ID>
    aadTenantId = <Directory (tenant) ID>
    

Limpieza de recursos

Si quiere limpiar y quitar una suscripción a Communication Services, puede eliminar el recurso o grupo de recursos. Al eliminar el grupo de recursos, también se elimina cualquier otro recurso que esté asociado a él. Obtenga más información sobre la limpieza de recursos.

Pasos siguientes

Para más información, consulte los siguientes artículos.

Lecturas adicionales

El ejemplo de elementos principales de llamada de grupo para Android de Azure Communication Services muestra cómo usar el SDK de llamadas de Communication Services para Android para crear una experiencia de llamada de grupo que incluya voz y vídeo. En este inicio rápido de ejemplo, aprenderá a configurar y ejecutar el ejemplo. Se proporciona información general del ejemplo con fines de contextualización.

Descarga de código

Busque el proyecto de este ejemplo en GitHub.

Información general

El ejemplo consiste en una aplicación Android nativa que usa la biblioteca de cliente de la interfaz de usuario de Azure Communication Services para Android para crear una experiencia de llamada que incluya llamadas de voz y vídeo. La aplicación usa un componente de servidor para aprovisionar los tokens de acceso que, posteriormente, se usan para inicializar el SDK de Azure Communication Services. Para configurar este componente de servidor, siga el tutorial Creación de un servicio de autenticación de confianza mediante Azure Functions.

El ejemplo tendrá una apariencia similar a la siguiente:

Captura de pantalla que muestra la página de aterrizaje de la aplicación de ejemplo.

Al presionar el botón "Iniciar nueva llamada", la aplicación Android le pedirá que escriba el nombre para mostrar que se usará para la llamada.

Captura de pantalla que muestra la pantalla anterior a la llamada en la aplicación de ejemplo.

Después de pulsar "Siguiente" en la página "Iniciar una llamada", podrá compartir el "Id. de llamada de grupo".

Captura de pantalla que muestra la pantalla para compartir el Id. de llamada de grupo en la aplicación de ejemplo.

La aplicación también le permite unirse a una llamada existente de Azure Communication Services al especificar el identificador de esa llamada o el vínculo de identificador y el nombre para mostrar de la reunión de Teams.

Captura de pantalla que muestra la pantalla para unirse a una llamada en la aplicación de ejemplo.

Después de unirse a una llamada, se le solicitará que conceda permiso a la aplicación para acceder a la cámara y el micrófono si aún no están autorizados. Verá el panel de llamada principal en el que reside la experiencia de llamada.

Captura de pantalla que muestra la pantalla principal de la aplicación de ejemplo.

Componentes de la pantalla principal de llamada:

  • Galería multimedia: la fase principal en la que se muestran los participantes. Si un participante tiene habilitada la cámara, aquí se muestra su fuente de vídeo. Cada participante tiene un icono individual con su nombre para mostrar y la secuencia de vídeo (si la hay). La galería admite varios participantes y se actualiza al agregarlos o al quitarlos de la llamada.
  • Barra de acciones: Aquí es donde se encuentran los principales controles de llamada. Estos controles permiten activar o desactivar el vídeo y el micrófono, compartir la pantalla y abandonar la llamada.

A continuación encontrará más información sobre los requisitos previos y los pasos para configurar el ejemplo.

Requisitos previos

Ejecución local del ejemplo

El ejemplo de llamada de grupo se puede ejecutar localmente mediante Android Studio. Los desarrolladores pueden usar su dispositivo físico o un emulador para probar la aplicación.

Antes de ejecutar el ejemplo por primera vez

  1. Abra Android Studio y seleccione Open an Existing Project.
  2. Abra la carpeta AzureCalling dentro de la versión descargada para el ejemplo.
  3. Expanda las aplicaciones o recursos para actualizar appSettings.properties. Establezca el valor de la clave communicationTokenFetchUrl para que sea la dirección URL del punto de conexión de autenticación configurado como requisito previo.

Ejecución del ejemplo

Compile y ejecute el ejemplo en Android Studio.

(Opcional) Protección de un punto de conexión de autenticación

Con fines de demostración, este ejemplo usa un punto de conexión de acceso público de forma predeterminada para capturar un token de Azure Communication Services. En escenarios de producción, se recomienda usar un punto de conexión propio protegido para aprovisionar los tokens.

Mediante alguna configuración adicional, este ejemplo permite conectarse a un punto de conexión protegido de Microsoft Entra ID (Microsoft Entra ID) de manera que se solicite el inicio de sesión de usuario para que la aplicación capture un token de Azure Communication Services. Consulte los siguientes pasos:

  1. Habilite la autenticación de Microsoft Entra en la aplicación.

  2. Vaya a la página de información general de la aplicación registrada en Registros de aplicaciones de Microsoft Entra. Anote los valores de Package name, Signature hash, MSAL Configutaion.

Configuración de Microsoft Entra en Azure Portal.

  1. Edición de AzureCalling/app/src/main/res/raw/auth_config_single_account.json y establecimiento de isAADAuthEnabled para habilitar Microsoft Entra ID.

  2. Edite AndroidManifest.xml y establezca android:path en el hash de firma del almacén de claves. (Opcional. El valor actual usa un hash de tipo debug.keystore que está incluido. Si se usa un almacén de claves diferente, debe actualizarse).

    <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. Copie la configuración de MSAL para Android desde Azure Portal y péguela en AzureCalling/app/src/main/res/raw/auth_config_single_account.json. Incluya "account_mode": "SINGLE".

       {
          "client_id": "",
          "authorization_user_agent": "DEFAULT",
          "redirect_uri": "",
          "account_mode" : "SINGLE",
          "authorities": [
             {
                "type": "AAD",
                "audience": {
                "type": "AzureADMyOrg",
                "tenant_id": ""
                }
             }
          ]
       }
    
  4. Edite AzureCalling/app/src/main/res/raw/auth_config_single_account.json y establezca el valor de la clave communicationTokenFetchUrl para que sea la dirección URL del punto de conexión de autenticación.

  5. Edite AzureCalling/app/src/main/res/raw/auth_config_single_account.json y establezca el valor de la clave aadScopes desde los ámbitos Expose an API de Azure Active Directory

  6. Establezca el valor de graphURL en AzureCalling/app/assets/appSettings.properties como punto de conexión de Graph API para capturar información del usuario.

  7. Edite AzureCalling/app/src/main/assets/appSettings.properties y establezca el valor de la clave tenant para habilitar el inicio de sesión silencioso para que el usuario no tenga que autenticarse de nuevo y de nuevo al reiniciar la aplicación.

Limpieza de recursos

Si quiere limpiar y quitar una suscripción a Communication Services, puede eliminar el recurso o grupo de recursos. Al eliminar el grupo de recursos, también se elimina cualquier otro recurso que esté asociado a él. Obtenga más información sobre la limpieza de recursos.

Pasos siguientes

Para más información, consulte los siguientes artículos.

Lecturas adicionales

El ejemplo de elementos principales de llamada de grupo para Windows de Azure Communication Services muestra cómo usar Calling SDK de Communication Services para Windows para crear una experiencia de llamada de grupo que incluya voz y vídeo. En este ejemplo, aprenderá a configurar y ejecutar el ejemplo. Se proporciona información general del ejemplo con fines de contextualización.

En este artículo de inicio rápido aprenderá a iniciar una videollamada 1:1 con el SDK de llamada de Azure Communication Services para Windows.

Código de ejemplo de UWP

Requisitos previos

Para completar este tutorial, debe cumplir los siguientes requisitos previos:

Instalación

Creación del proyecto

En Visual Studio, cree un proyecto con la plantilla Aplicación vacía (Windows universal) para configurar una aplicación para la Plataforma universal de Windows (UWP) de una sola página.

Captura de pantalla que muestra la ventana Nuevo proyecto de UWP en Visual Studio.

Instalar el paquete

Haga clic con el botón derecho en su proyecto y vaya a Manage Nuget Packages para instalar la versión Azure.Communication.Calling.WindowsClient1.2.0-beta.1 o superior. Asegúrese de que la casilla Incluir versión preliminar esté activada.

Solicitar acceso

Vaya a Package.appxmanifest y haga clic en Capabilities. Compruebe Internet (Client & Server) para obtener acceso entrante y saliente a Internet. Compruebe Microphone para acceder a la fuente de audio del micrófono. Compruebe WebCam para acceder a la cámara del dispositivo.

Agregue el código siguiente a Package.appxmanifest, para lo que debe clic con el botón derecho y elegir View Code (Ver código).

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

Instalación del marco de la aplicación

Es necesario configurar un diseño básico para adjuntar la lógica. Para hacer una llamada saliente, se necesita un elemento TextBox para proporcionar el id. de usuario del destinatario. También se necesita un botón Start Call y un botón Hang Up. También es necesario obtener una vista previa del vídeo local y representar el vídeo remoto del otro participante. Por lo tanto, necesitamos dos elementos para mostrar las secuencias de vídeo.

Abra MainPage.xaml del proyecto y reemplace el contenido por la siguiente implementación.

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

Ábralo en App.xaml.cs (haga clic con el botón derecho y elija Ver código) y agregue esta línea al principio:

using CallingQuickstart;

Abra MainPage.xaml.cs (haga clic con el botón derecho y elija Ver código) y reemplace el contenido por la siguiente implementación:

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

Modelo de objetos

Las siguientes clases e interfaces controlan algunas de las características principales del SDK de llamadas de Azure Communication Services:

Nombre Descripción
CallClient CallClient es el punto de entrada principal a la biblioteca cliente de llamadas.
CallAgent CallAgent se usa para iniciar llamadas y unirse a estas.
CommunicationCall CommunicationCall se usa para administrar las llamadas realizadas o a las que se ha unido.
CallTokenCredential CallTokenCredential se usa como la credencial del token para crear una instancia de CallAgent.
CommunicationUserIdentifier CommunicationUserIdentifier se usa para representar la identidad del usuario, que puede ser una de las opciones siguientes: CommunicationUserIdentifier, PhoneNumberIdentifier o CallingApplication.

Autenticar el cliente

Para inicializar CallAgent, necesita un token de acceso de usuario. Por lo general, este token se genera desde un servicio con autenticación específica de la aplicación. Para obtener más información sobre los tokens de acceso de usuario, consulte la guía Tokens de acceso de usuario.

En el inicio rápido, reemplace <AUTHENTICATION_TOKEN> por un token de acceso de usuario generado para el recurso de Azure Communication Service.

Una vez que tenga un token, inicialice una instancia de CallAgent con él, lo que nos permite realizar y recibir llamadas. Para acceder a las cámaras del dispositivo, también es necesario obtener una instancia de Administrador de dispositivos.

Agregue el siguiente código a la función 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
    }
}

Inicio de una llamada con vídeo

Agregue la implementación a CallButton_Click para iniciar una llamada con vídeo. Es necesario enumerar las cámaras con la instancia del administrador de dispositivos y construir LocalOutgoingVideoStream. Es necesario establecer VideoOptions con LocalVideoStream y pasarlo con startCallOptions para establecer las opciones iniciales de la llamada. Al adjuntar LocalOutgoingVideoStream a MediaElement, podemos ver la vista previa del vídeo local.

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

Agregue los métodos para iniciar o unirse a los diferentes tipos de llamada (llamada de Azure Communication Services individual, llamada telefónica individual, llamada de grupo de Azure Communication Services, unión a reuniones de Teams, etc.).

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

Agregue el código para crear LocalVideoStream en función de la cámara seleccionada en el método 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);
}

Aceptar una llamada entrante

Agregue la implementación a OnIncomingCallAsync para responder a una llamada entrante con vídeo y pase LocalVideoStream a acceptCallOptions.

var incomingCall = args.IncomingCall;

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

_ = await incomingCall.AcceptAsync(acceptCallOptions);

Secuencias de vídeo remotas y participantes remotos

Todos los participantes remotos están disponibles mediante la colección RemoteParticipants de una instancia de la llamada. Una vez conectada la llamada, podemos acceder a los participantes remotos de la llamada y controlar las secuencias de vídeo remotas.


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

}

Representación de vídeos remotos

Adjunte a MediaElement cada secuencia de vídeo remota.

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

Actualización del estado de la llamada

Necesitamos limpiar los representadores de vídeo una vez desconectada la llamada y controlar el caso cuando los participantes remotos se unen inicialmente a la llamada.

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

Finalizar una llamada

Finalice la llamada actual cuando se haga clic en el botón Hang Up. Agregue la implementación a HangupButton_Click para finalizar una llamada con el callAgent que creamos y desmonte la actualización del participante y llame a los controladores de eventos de estado de llamada.

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

Ejecución del código

Puede compilar y ejecutar el código en Visual Studio. Para las plataformas de la solución, se admiten ARM64, x64 y x86.

Para hacer una llamada de vídeo saliente, proporcione un id. de usuario en el campo de texto y haga clic en el botón Start Call.

Nota: Al llamar a 8:echo123 se detiene la secuencia de vídeo porque echo bot no admite el streaming de vídeo.

Para más información sobre los identificadores de usuario, consulte la guía Tokens de acceso de usuario.

Código de ejemplo de WinUI 3

Requisitos previos

Para completar este tutorial, debe cumplir los siguientes requisitos previos:

Instalación

Creación del proyecto

En Visual Studio, cree un nuevo proyecto con la plantilla Aplicación en blanco, empaquetada (WinUI 3 en escritorio) para configurar una aplicación WinUI 3 de una sola página.

Captura de pantalla que muestra la ventana Nuevo proyecto de WinUI en Visual Studio.

Instalar el paquete

Haga clic con el botón derecho en su proyecto y vaya a Manage Nuget Packages para instalar la versión Azure.Communication.Calling.WindowsClient1.0.0 o superior. Asegúrese de que la casilla Incluir versión preliminar esté activada.

Solicitar acceso

Captura de pantalla que muestra la solicitud de acceso a Internet y al micrófono en Visual Studio.

Agregue el siguiente código a su app.manifest:

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

Instalación del marco de la aplicación

Es necesario configurar un diseño básico para adjuntar la lógica. Para hacer una llamada saliente, se necesita un elemento TextBox para proporcionar el id. de usuario del destinatario. También se necesita un botón Start Call y un botón Hang Up. También es necesario obtener una vista previa del vídeo local y representar el vídeo remoto del otro participante. Por lo tanto, necesitamos dos elementos para mostrar las secuencias de vídeo.

Abra MainWindow.xaml del proyecto y reemplace el contenido por la siguiente implementación.

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

Ábralo en App.xaml.cs (haga clic con el botón derecho y elija Ver código) y agregue esta línea al principio:

using CallingQuickstart;

Abra MainWindow.xaml.cs (haga clic con el botón derecho y elija Ver código) y reemplace el contenido por la siguiente implementación:

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

Modelo de objetos

Las siguientes clases e interfaces controlan algunas de las características principales del SDK de llamadas de Azure Communication Services:

Nombre Descripción
CallClient CallClient es el punto de entrada principal a la biblioteca cliente de llamadas.
CallAgent CallAgent se usa para iniciar llamadas y unirse a estas.
CommunicationCall CommunicationCall se usa para administrar las llamadas realizadas o a las que se ha unido.
CallTokenCredential CallTokenCredential se usa como la credencial del token para crear una instancia de CallAgent.
CommunicationUserIdentifier CommunicationUserIdentifier se usa para representar la identidad del usuario, que puede ser una de las opciones siguientes: CommunicationUserIdentifier, PhoneNumberIdentifier o CallingApplication.

Autenticar el cliente

Para inicializar CallAgent, necesita un token de acceso de usuario. Por lo general, este token se genera desde un servicio con autenticación específica de la aplicación. Para obtener más información sobre los tokens de acceso de usuario, consulte la guía Tokens de acceso de usuario.

En el inicio rápido, reemplace <AUTHENTICATION_TOKEN> por un token de acceso de usuario generado para el recurso de Azure Communication Service.

Una vez que tenga un token, inicialice una instancia de CallAgent con él, lo que nos permite realizar y recibir llamadas. Para acceder a las cámaras del dispositivo, también es necesario obtener una instancia de Administrador de dispositivos.

Agregue el siguiente código a la función 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;

Inicio de una llamada con vídeo

Agregue la implementación a CallButton_Click para iniciar una llamada con vídeo. Es necesario enumerar las cámaras con la instancia del administrador de dispositivos y construir LocalVideoStream. Es necesario establecer VideoOptions con LocalVideoStream y pasarlo con startCallOptions para establecer las opciones iniciales de la llamada. Al adjuntar LocalVideoStream a MediaPlayerElement, podemos ver la vista previa del vídeo local.

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;

Aceptar una llamada entrante

Agregue la implementación a Agent_OnIncomingCallAsync para responder a una llamada entrante con vídeo y pase LocalVideoStream a 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);

Secuencias de vídeo remotas y participantes remotos

Todos los participantes remotos están disponibles mediante la colección RemoteParticipants de una instancia de la llamada. Una vez conectada la llamada, podemos acceder a los participantes remotos de la llamada y controlar las secuencias de vídeo remotas.

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

Representación de vídeos remotos

Adjunte a MediaPlayerElement cada secuencia de vídeo remota.

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

Actualización del estado de la llamada

Necesitamos limpiar los representadores de vídeo una vez desconectada la llamada y controlar el caso cuando los participantes remotos se unen inicialmente a la llamada.

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

Finalizar una llamada

Finalice la llamada actual cuando se haga clic en el botón Hang Up. Agregue la implementación a HangupButton_Click para finalizar una llamada con el callAgent que creamos y desmonte la actualización del participante y llame a los controladores de eventos de estado de llamada.

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

Ejecución del código

Puede compilar y ejecutar el código en Visual Studio. Para las plataformas de la solución, se admiten ARM64, x64 y x86.

Para hacer una llamada de vídeo saliente, proporcione un id. de usuario en el campo de texto y haga clic en el botón Start Call.

Nota: Al llamar a 8:echo123 se detiene la secuencia de vídeo porque echo bot no admite el streaming de vídeo.

Para más información sobre los identificadores de usuario, consulte la guía Tokens de acceso de usuario.