Introdução ao exemplo de herói chamador

O Exemplo de Herói de Chamada de Grupo dos Serviços de Comunicação do Azure demonstra como o SDK da Web de Chamada dos Serviços de Comunicação pode ser usado para criar uma experiência de chamada em grupo.

Neste Guia de início rápido de exemplo, aprendemos como o exemplo funciona antes de executá-lo em sua máquina local e, em seguida, implantá-lo no Azure usando seus próprios recursos dos Serviços de Comunicação do Azure.

Transferir código

Encontre o projeto para este exemplo no GitHub. Uma versão do exemplo que inclui recursos atualmente em visualização pública, como Interoperabilidade do Teams e Gravação de Chamadas, pode ser encontrada em uma ramificação separada.

Implementar no Azure

Descrição geral

O exemplo tem um aplicativo do lado do cliente e um aplicativo do lado do servidor. O aplicativo do lado do cliente é um aplicativo Web React/Redux que usa a estrutura de interface do usuário Fluent da Microsoft. Este aplicativo envia solicitações para um aplicativo do lado do servidor ASP.NET Core que ajuda o aplicativo do lado do cliente a se conectar ao Azure.

Veja como é o exemplo:

Captura de tela mostrando a página de destino do aplicativo de exemplo.

Quando você pressiona o botão "Iniciar uma chamada", o aplicativo Web busca um token de acesso de usuário do aplicativo do lado do servidor. Esse token é usado para conectar o aplicativo cliente aos Serviços de Comunicação do Azure. Depois que o token for recuperado, você será solicitado a especificar a câmera e o microfone que deseja usar. Pode desativar/ativar os seus dispositivos com controlos de alternância:

Captura de tela mostrando a tela de pré-chamada do aplicativo de exemplo.

Depois de configurar seu nome de exibição e dispositivos, você pode ingressar na sessão de chamada. Você verá a tela de chamada principal onde mora a experiência principal de chamada.

Captura de tela mostrando a tela principal do aplicativo de exemplo.

Componentes da tela principal de chamada:

  • Galeria de Mídia: O palco principal onde os participantes são mostrados. Se um participante tiver a câmara ativada, o feed de vídeo é apresentado aqui. Cada participante tem um bloco individual que mostra seu nome de exibição e fluxo de vídeo (quando houver)
  • Cabeçalho: É aqui que os principais controles de chamada estão localizados para alternar as configurações e a barra lateral do participante, ativar e desativar o vídeo, compartilhar a tela e sair da chamada.
  • Barra lateral: é onde as informações de participantes e configurações são mostradas quando alternados usando os controles no cabeçalho. O componente pode ser descartado usando o 'X' no canto superior direito. A barra lateral dos participantes mostra uma lista de participantes e um link para convidar mais usuários para o bate-papo. A barra lateral de configurações permite que você defina as configurações do microfone e da câmera.

Abaixo, você pode encontrar mais informações sobre pré-requisitos e etapas para configurar o exemplo.

Pré-requisitos

Antes de executar a amostra pela primeira vez

  1. Abra uma instância do PowerShell, Terminal do Windows, Prompt de Comando ou equivalente e navegue até o diretório para o qual você deseja clonar o exemplo.

    git clone https://github.com/Azure-Samples/communication-services-web-calling-hero.git
    
  2. Obtenha o Connection String no portal do Azure ou usando a CLI do Azure.

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

    Para obter mais informações sobre cadeias de conexão, consulte Criar recursos de comunicação do Azure

  3. Depois de obter o Connection Stringarquivo , adicione a cadeia de conexão ao arquivo samples/Server/appsetting.json . Insira sua cadeia de conexão na variável: ResourceConnectionString.

  4. Obtenha o Endpoint string no portal do Azure ou usando a CLI do Azure.

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

    Para obter mais informações sobre cadeias de caracteres de ponto de extremidade, consulte Criar recursos de comunicação do Azure

  5. Depois de obter o Endpoint Stringarquivo , adicione a cadeia de caracteres do ponto de extremidade ao arquivo samples/Server/appsetting.json . Insira sua cadeia de caracteres de ponto de extremidade na variável EndpointUrl

Execução local

  1. Instalar dependências

    npm run setup
    
  2. Iniciar o aplicativo de chamada

    npm run start
    

    Isso abre um servidor cliente na porta 3000 que serve os arquivos do site e um servidor de api na porta 8080 que executa funcionalidades como cunhar tokens para participantes de chamadas.

Resolução de Problemas

  • O aplicativo mostra uma tela "Navegador não suportado", mas estou em um navegador compatível.

    Se o seu aplicativo estiver sendo servido por um nome de host diferente de localhost, você deverá fornecer tráfego por https e não http.

Publicar no Azure

  1. npm run setup
  2. npm run build
  3. npm run package
  4. Use a extensão do Azure e implante o diretório Calling/dist em seu serviço de aplicativo

Clean up resources (Limpar recursos)

Se quiser limpar e remover uma assinatura dos Serviços de Comunicação, você pode excluir o recurso ou grupo de recursos. A exclusão do grupo de recursos também exclui quaisquer outros recursos associados a ele. Saiba mais sobre a limpeza de recursos.

Próximos passos

Para obter mais informações, consulte os seguintes artigos:

Leitura adicional

  • Exemplos - Encontre mais exemplos e exemplos na nossa página de visão geral de exemplos.
  • Redux - Gerenciamento de estado do lado do cliente
  • FluentUI - Biblioteca de interface do usuário com tecnologia Microsoft
  • React - Biblioteca para a criação de interfaces de utilizador
  • ASP.NET Core - Framework para a construção de aplicações web

O Exemplo de Herói de Chamada de Grupo dos Serviços de Comunicação do Azure para iOS demonstra como o SDK do iOS de Chamada de Serviços de Comunicação pode ser usado para criar uma experiência de chamada em grupo que inclui voz e vídeo. Neste exemplo de início rápido, você aprenderá como configurar e executar o exemplo. Uma visão geral do exemplo é fornecida para contextualização.

Transferir código

Encontre o projeto para este exemplo no GitHub.

Descrição geral

O exemplo é um aplicativo iOS nativo que usa os SDKs iOS dos Serviços de Comunicação do Azure para criar uma experiência de chamada que apresenta chamadas de voz e vídeo. O aplicativo usa um componente do lado do servidor para provisionar tokens de acesso que são usados para inicializar o SDK dos Serviços de Comunicação do Azure. Para configurar esse componente do lado do servidor, sinta-se à vontade para seguir o tutorial Serviço Confiável com Funções do Azure.

Veja como é o exemplo:

Captura de tela mostrando a página de destino do aplicativo de exemplo.

Quando você pressiona o botão "Iniciar nova chamada", o aplicativo iOS solicita que você insira seu nome de exibição para usar na chamada.

Captura de tela mostrando a tela de pré-chamada do aplicativo de exemplo.

Depois de tocar em "Seguinte" no ecrã "Iniciar chamada", tem a oportunidade de partilhar o ID de grupo da chamada através da folha de partilha do iOS.

Captura de tela mostrando a tela ID do grupo de compartilhamento do aplicativo de exemplo.

O aplicativo também permite que você ingresse em uma chamada existente dos Serviços de Comunicação do Azure especificando o ID da chamada existente ou o link de ID de equipe.

Captura de tela mostrando a tela de chamada de ingresso do aplicativo de exemplo.

Depois de participar de uma chamada, você será solicitado a dar permissão ao aplicativo para acessar sua câmera e microfone, se ainda não estiver autorizado. Tenha em mente que, como todos os aplicativos baseados em AVFoundation, a verdadeira funcionalidade de áudio e vídeo só está disponível em hardware real.

Depois de configurar seu nome de exibição e participar da chamada, você verá a tela principal da chamada onde reside a experiência principal de chamada.

Captura de tela mostrando a tela principal do aplicativo de exemplo.

Componentes da tela principal de chamada:

  • Galeria de Mídia: O palco principal onde os participantes são mostrados. Se um participante tiver a câmara ativada, o feed de vídeo é apresentado aqui. Cada participante tem um bloco individual que mostra seu nome de exibição e fluxo de vídeo (quando houver). A galeria suporta vários participantes e é atualizada quando os participantes são adicionados ou removidos para a chamada.
  • Barra de Ação: é onde os principais controles de chamada estão localizados. Estes controlos permitem-lhe ligar/desligar o vídeo e o microfone, partilhar o ecrã e sair da chamada.

Abaixo, você encontrará mais informações sobre pré-requisitos e etapas para configurar o exemplo.

Pré-requisitos

  • Uma conta do Azure com uma subscrição ativa. Para obter detalhes, consulte Criar uma conta gratuitamente.
  • Um Mac com Xcode, juntamente com um certificado de programador válido instalado no seu Porta-chaves.
  • Um recurso dos Serviços de Comunicação do Azure. Para obter detalhes, consulte Criar um recurso dos Serviços de Comunicação do Azure.
  • Uma Função do Azure executando o Ponto de Extremidade de Autenticação para buscar tokens de acesso.

Executando a amostra localmente

O exemplo de chamada de grupo pode ser executado localmente usando XCode. Os desenvolvedores podem usar seu dispositivo físico ou um emulador para testar o aplicativo.

Antes de executar a amostra pela primeira vez

  1. Instale dependências executando pod installo .
  2. Abra AzureCalling.xcworkspace no XCode.
  3. Crie um arquivo de texto na raiz, chame AppSettings.xcconfig e defina o valor:
    communicationTokenFetchUrl = <your authentication endpoint, without the https:// component>
    

Executar amostra

Crie e execute o exemplo no XCode, usando o destino AzureCalling no simulador ou dispositivo de sua escolha.

(Opcional) Protegendo um ponto de extremidade de autenticação

Para fins de demonstração, este exemplo usa um ponto de extremidade acessível publicamente por padrão para buscar um token de acesso dos Serviços de Comunicação do Azure. Para cenários de produção, recomendamos usar seu próprio endpoint seguro para provisionar seus próprios tokens.

Com configuração adicional, este exemplo dá suporte à conexão a um ponto de extremidade protegido pelo Microsoft Entra ID (ID do Microsoft Entra) para que o logon do usuário seja necessário para que o aplicativo busque um token de acesso dos Serviços de Comunicação do Azure. Veja os passos abaixo:

  1. Habilite a autenticação do Microsoft Entra em seu aplicativo.
  2. Vá para a página de visão geral do aplicativo registrado em Registros de aplicativos do Microsoft Entra. Tome nota do Application (client) ID, Directory (tenant) ID, Application ID URI

Configuração do Microsoft Entra no portal do Azure.

  1. Crie um AppSettings.xcconfig arquivo na raiz, se ainda não estiver presente, e adicione os valores:
    communicationTokenFetchUrl = <Application ID URI, without the https:// component>
    aadClientId = <Application (client) ID>
    aadTenantId = <Directory (tenant) ID>
    

Clean up resources (Limpar recursos)

Se quiser limpar e remover uma assinatura dos Serviços de Comunicação, você pode excluir o recurso ou grupo de recursos. A exclusão do grupo de recursos também exclui quaisquer outros recursos associados a ele. Saiba mais sobre a limpeza de recursos.

Próximos passos

Para obter mais informações, consulte os seguintes artigos:

Leitura adicional

O Exemplo de Herói de Chamada de Grupo dos Serviços de Comunicação do Azure para Android demonstra como o SDK de Chamada de Serviços de Comunicação para Android pode ser usado para criar uma experiência de chamada em grupo que inclua voz e vídeo. Neste exemplo de início rápido, você aprenderá como configurar e executar o exemplo. Uma visão geral do exemplo é fornecida para contextualização.

Transferir código

Encontre o projeto para este exemplo no GitHub.

Descrição geral

O exemplo é um aplicativo Android nativo que usa a biblioteca de cliente da interface do usuário do Android dos Serviços de Comunicação do Azure para criar uma experiência de chamada que apresenta chamadas de voz e vídeo. O aplicativo usa um componente do lado do servidor para provisionar tokens de acesso que são usados para inicializar o SDK dos Serviços de Comunicação do Azure. Para configurar esse componente do lado do servidor, sinta-se à vontade para seguir o tutorial Serviço Confiável com Funções do Azure.

Veja como é o exemplo:

Captura de tela mostrando a página de destino do aplicativo de exemplo.

Quando você pressiona o botão "Iniciar nova chamada", o aplicativo Android solicita que você insira seu nome de exibição para usar na chamada.

Captura de tela mostrando a tela de pré-chamada do aplicativo de exemplo.

Depois de tocar em "Seguinte" na página "Iniciar uma chamada", tem a oportunidade de partilhar o "ID de Chamada de Grupo".

Captura de tela mostrando a tela de ID de chamada de grupo de compartilhamento do aplicativo de exemplo.

O aplicativo permite que você ingresse em uma chamada existente dos Serviços de Comunicação do Azure especificando a ID da chamada existente ou o link e o nome para exibição da ID da reunião das equipes.

Captura de tela mostrando a tela de chamada de ingresso do aplicativo de exemplo.

Depois de participar de uma chamada, você será solicitado a dar permissão ao aplicativo para acessar sua câmera e microfone, se ainda não estiver autorizado. Você verá a tela de chamada principal onde vive a experiência principal de chamada.

Captura de tela mostrando a tela principal do aplicativo de exemplo.

Componentes da tela principal de chamada:

  • Galeria de Mídia: O palco principal onde os participantes são mostrados. Se um participante tiver a câmara ativada, o feed de vídeo é apresentado aqui. Cada participante tem um bloco individual que mostra seu nome de exibição e fluxo de vídeo (quando houver). A galeria suporta vários participantes e é atualizada quando os participantes são adicionados ou removidos para a chamada.
  • Barra de Ação: é onde os principais controles de chamada estão localizados. Estes controlos permitem-lhe ligar/desligar o vídeo e o microfone, partilhar o ecrã e sair da chamada.

Abaixo, você encontrará mais informações sobre pré-requisitos e etapas para configurar o exemplo.

Pré-requisitos

  • Uma conta do Azure com uma subscrição ativa. Para obter detalhes, consulte Criar uma conta gratuitamente.
  • Android Studio em execução no seu computador
  • Um recurso dos Serviços de Comunicação do Azure. Para obter detalhes, consulte Criar um recurso dos Serviços de Comunicação do Azure.
  • Uma Função do Azure executando o Ponto de Extremidade de Autenticação para buscar tokens de acesso.

Executando a amostra localmente

O exemplo de chamada em grupo pode ser executado localmente usando o Android Studio. Os desenvolvedores podem usar seu dispositivo físico ou um emulador para testar o aplicativo.

Antes de executar a amostra pela primeira vez

  1. Abra o Android Studio e selecione Open an Existing Project
  2. Abra a pasta dentro da AzureCalling versão baixada para o exemplo.
  3. Expanda o aplicativo/ativos para atualizar appSettings.propertieso . Defina o valor da chave communicationTokenFetchUrl para ser a URL do seu Ponto de Extremidade de Autenticação configurado como um pré-requisito.

Executar amostra

Crie e execute o exemplo no Android Studio.

(Opcional) Protegendo um ponto de extremidade de autenticação

Para fins de demonstração, este exemplo usa um ponto de extremidade acessível publicamente por padrão para buscar um token dos Serviços de Comunicação do Azure. Para cenários de produção, recomendamos usar seu próprio endpoint seguro para provisionar seus próprios tokens.

Com configuração adicional, este exemplo dá suporte à conexão a um ponto de extremidade protegido pelo Microsoft Entra ID (ID do Microsoft Entra) para que o logon do usuário seja necessário para que o aplicativo busque um token dos Serviços de Comunicação do Azure. Veja os passos abaixo:

  1. Habilite a autenticação do Microsoft Entra em seu aplicativo.

  2. Vá para a página de visão geral do aplicativo registrado em Registros de aplicativos do Microsoft Entra. Tome nota do Package name, Signature hash, MSAL Configutaion.

Configuração do Microsoft Entra no portal do Azure.

  1. Edite AzureCalling/app/src/main/res/raw/auth_config_single_account.json e defina isAADAuthEnabled para ativar o Microsoft Entra ID.

  2. Edite AndroidManifest.xml e defina android:path como o hash de assinatura do keystore. (Opcional. O valor atual usa hash do bundled debug.keystore. Se for usado um armazenamento de chaves diferente, isso deverá ser atualizado.)

    <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 a configuração do MSAL Android do portal do Azure e cole no AzureCalling/app/src/main/res/raw/auth_config_single_account.json. Incluir "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 e defina o valor da chave communicationTokenFetchUrl para ser a URL do seu ponto de extremidade de autenticação seguro.

  5. Editar AzureCalling/app/src/main/res/raw/auth_config_single_account.json e definir o valor para a chave aadScopes a partir de Azure Active Directory Expose an API escopos

  6. Defina o valor para graphURL in AzureCalling/app/assets/appSettings.properties como o ponto de extremidade da API do Graph para buscar informações do usuário.

  7. Edite AzureCalling/app/src/main/assets/appSettings.properties e defina o valor da chave tenant para habilitar o login silencioso para que o usuário não precise ser autenticado repetidamente ao reiniciar o aplicativo.

Clean up resources (Limpar recursos)

Se quiser limpar e remover uma assinatura dos Serviços de Comunicação, você pode excluir o recurso ou grupo de recursos. A exclusão do grupo de recursos também exclui quaisquer outros recursos associados a ele. Saiba mais sobre a limpeza de recursos.

Próximos passos

Para obter mais informações, consulte os seguintes artigos:

Leitura adicional

O Exemplo de Herói de Chamada de Grupo dos Serviços de Comunicação do Azure para Windows demonstra como o SDK do Windows de Chamada de Serviços de Comunicação pode ser usado para criar uma experiência de chamada em grupo que inclui voz e vídeo. Neste exemplo, você aprenderá a configurar e executar o exemplo. Uma visão geral do exemplo é fornecida para contextualização.

Neste guia de início rápido, você aprenderá a iniciar uma chamada de vídeo 1:1 usando o SDK de Chamada dos Serviços de Comunicação do Azure para Windows.

Código de exemplo UWP

Pré-requisitos

Para concluir este tutorial, precisa dos seguintes pré-requisitos:

  • Uma conta do Azure com uma subscrição ativa. Crie uma conta gratuitamente.

  • Instale o Visual Studio 2022 com a carga de trabalho de desenvolvimento da Plataforma Universal do Windows.

  • Um recurso de Serviços de Comunicação implantado. Crie um recurso de Serviços de Comunicação. Você precisa gravar sua cadeia de conexão para este início rápido.

  • Um Token de Acesso de Usuário para seu Serviço de Comunicação do Azure. Você também pode usar a CLI do Azure e executar o comando com sua cadeia de conexão para criar um usuário e um token de acesso.

    az communication identity token issue --scope voip --connection-string "yourConnectionString"
    

    Para obter detalhes, consulte Usar a CLI do Azure para criar e gerenciar tokens de acesso.

Configuração

Criação do projeto

No Visual Studio, crie um novo projeto com o modelo Aplicativo em Branco (Universal Windows) para configurar um aplicativo da Plataforma Universal do Windows (UWP) de página única.

Captura de tela mostrando a janela Novo Projeto UWP no Visual Studio.

Instalar o pacote

Clique com o botão direito do mouse em seu projeto e vá para Manage Nuget Packages instalar Azure.Communication.Calling.WindowsClient a versão 1.2.0-beta.1 ou superior. Certifique-se de que a opção Incluir pré-liberado está marcada.

Pedir acesso

Vá para Package.appxmanifest e clique em Capabilities. Verifique Internet (Client & Server) para obter acesso de entrada e saída à Internet. Verifique Microphone para acessar o feed de áudio do microfone. Verifique WebCam para acessar a câmera do dispositivo.

Adicione o seguinte código ao seu Package.appxmanifest clicando com o botão direito do mouse e escolhendo View Code.

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

Configurar a estrutura do aplicativo

Precisamos configurar um layout básico para anexar nossa lógica. Para fazer uma chamada de saída, precisamos de um TextBox para fornecer o ID de usuário do destinatário. Precisamos também de um Start Call botão e de um Hang Up botão. Também precisamos visualizar o vídeo local e renderizar o vídeo remoto do outro participante. Portanto, precisamos de dois elementos para exibir os fluxos de vídeo.

Abra o MainPage.xaml do seu projeto e substitua o conteúdo pela seguinte implementação.

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

Abra para App.xaml.cs (clique com o botão direito do mouse e escolha View Code) e adicione esta linha ao topo:

using CallingQuickstart;

Abra o (clique com o botão direito do MainPage.xaml.cs mouse e escolha View Code) e substitua o conteúdo pela seguinte implementação:

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 objeto

As seguintes classes e interfaces lidam com alguns dos principais recursos do SDK de Chamada dos Serviços de Comunicação do Azure:

Nome Descrição
CallClient O CallClient é o principal ponto de entrada para a biblioteca de cliente de chamada.
CallAgent O CallAgent é usado para iniciar e participar de chamadas.
CommunicationCall O CommunicationCall é usado para gerenciar chamadas feitas ou associadas.
CallTokenCredential O CallTokenCredential é usado como a credencial de token para instanciar o CallAgent.
CommunicationUserIdentifier O CommunicationUserIdentifier é usado para representar a identidade do usuário, que pode ser uma das seguintes opções: CommunicationUserIdentifier, PhoneNumberIdentifier ou CallingApplication.

Autenticar o cliente

Para inicializar um CallAgent, você precisa de um Token de Acesso de Usuário. Geralmente esse token é gerado a partir de um serviço com autenticação específica para o aplicativo. Para obter mais informações sobre tokens de acesso de usuário, consulte o guia Tokens de acesso de usuário.

Para o início rápido, substitua <AUTHENTICATION_TOKEN> por um token de acesso de usuário gerado para seu recurso do Serviço de Comunicação do Azure.

Depois de ter um token, inicialize uma CallAgent instância com ele, o que nos permite fazer e receber chamadas. Para acessar as câmeras no dispositivo, também precisamos obter a instância do Gerenciador de Dispositivos.

Adicione o seguinte código à InitCallAgentAndDeviceManagerAsync função.

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

Iniciar uma chamada com vídeo

Adicione a implementação ao CallButton_Click para iniciar uma chamada com vídeo. Precisamos enumerar as câmeras com a instância do gerenciador de dispositivos e construir LocalOutgoingVideoStream. Precisamos definir o VideoOptions com LocalVideoStream e passá-lo com startCallOptions para definir as opções iniciais para a chamada. Ao anexar LocalOutgoingVideoStream a um MediaElement, podemos ver a visualização do 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;
}

Adicione os métodos para iniciar ou ingressar nos diferentes tipos de chamada (chamada 1:1 dos Serviços de Comunicação do Azure, chamada telefônica 1:1, chamada do Grupo dos Serviços de Comunicação do Azure, participação na reunião do 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 } }
    };
}

Adicione o código para criar o LocalVideoStream dependendo da câmera selecionada no CameraList_SelectionChanged método.

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

Aceitar uma chamada recebida

Adicione a implementação ao OnIncomingCallAsync para atender uma chamada recebida com vídeo, passe o LocalVideoStream para acceptCallOptions.

var incomingCall = args.IncomingCall;

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

_ = await incomingCall.AcceptAsync(acceptCallOptions);

Participantes remotos e fluxos de vídeo remotos

Todos os participantes remotos estão disponíveis através da RemoteParticipants coleta em uma instância de chamada. Uma vez que a chamada se conecta (CallState.Connected), podemos acessar os participantes remotos da chamada e lidar com os fluxos de vídeo remotos.

Nota

Quando um usuário ingressa em uma chamada, ele pode acessar os participantes remotos atuais por meio da RemoteParticipants coleção. O RemoteParticipantsUpdated evento não será acionado para esses participantes existentes. Esse evento só será acionado quando um participante remoto entrar ou sair da chamada enquanto o usuário já estiver na chamada.


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

}

Renderizar vídeos remotos

Para cada fluxo de vídeo remoto, anexe-o MediaElementao .

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

Atualização do estado da chamada

Precisamos limpar os renderizadores de vídeo assim que a chamada for desconectada e lidar com o caso quando os participantes remotos entrarem inicialmente na chamada.

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

Terminar uma chamada

Termine a chamada atual quando o Hang Up botão for clicado. Adicione a implementação ao HangupButton_Click para encerrar uma chamada com o callAgent que criamos e destrua os manipuladores de eventos de atualização e estado de chamada do participante.

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

Executar o código

Você pode criar e executar o código no Visual Studio. Para plataformas de solução, suportamos ARM64e x86x64 .

Você pode fazer uma chamada de vídeo de saída fornecendo um ID de usuário no campo de texto e clicando no Start Call botão.

Nota: A chamada 8:echo123 interrompe o fluxo de vídeo porque o echo bot não suporta streaming de vídeo.

Para obter mais informações sobre IDs de usuário (identidade), consulte o guia de Tokens de Acesso de Usuário.

Código de exemplo do WinUI 3

Pré-requisitos

Para concluir este tutorial, precisa dos seguintes pré-requisitos:

Configuração

Criação do projeto

No Visual Studio, crie um novo projeto com o modelo Aplicativo em branco, empacotado (WinUI 3 na área de trabalho) para configurar um aplicativo WinUI 3 de página única.

Captura de tela mostrando a janela New WinUI Project no Visual Studio.

Instalar o pacote

Clique com o botão direito do mouse em seu projeto e vá para instalar Azure.Communication.Calling.WindowsClient a Manage Nuget Packages versão 1.0.0 ou superior. Certifique-se de que a opção Incluir pré-liberado está marcada.

Pedir acesso

Captura de tela mostrando a solicitação de acesso à Internet e ao microfone no Visual Studio.

Adicione o seguinte código ao seu app.manifest:

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

Configurar a estrutura do aplicativo

Precisamos configurar um layout básico para anexar nossa lógica. Para fazer uma chamada de saída, precisamos de um TextBox para fornecer o ID de usuário do destinatário. Precisamos também de um Start Call botão e de um Hang Up botão. Também precisamos visualizar o vídeo local e renderizar o vídeo remoto do outro participante. Portanto, precisamos de dois elementos para exibir os fluxos de vídeo.

Abra o MainWindow.xaml do seu projeto e substitua o conteúdo pela seguinte implementação.

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

Abra para App.xaml.cs (clique com o botão direito do mouse e escolha View Code) e adicione esta linha ao topo:

using CallingQuickstart;

Abra o (clique com o botão direito do MainWindow.xaml.cs mouse e escolha View Code) e substitua o conteúdo pela seguinte implementação:

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 objeto

As seguintes classes e interfaces lidam com alguns dos principais recursos do SDK de Chamada dos Serviços de Comunicação do Azure:

Nome Descrição
CallClient O CallClient é o principal ponto de entrada para a biblioteca de cliente de chamada.
CallAgent O CallAgent é usado para iniciar e participar de chamadas.
CommunicationCall O CommunicationCall é usado para gerenciar chamadas feitas ou associadas.
CallTokenCredential O CallTokenCredential é usado como a credencial de token para instanciar o CallAgent.
CommunicationUserIdentifier O CommunicationUserIdentifier é usado para representar a identidade do usuário, que pode ser uma das seguintes opções: CommunicationUserIdentifier, PhoneNumberIdentifier ou CallingApplication.

Autenticar o cliente

Para inicializar um CallAgent, você precisa de um Token de Acesso de Usuário. Geralmente esse token é gerado a partir de um serviço com autenticação específica para o aplicativo. Para obter mais informações sobre tokens de acesso de usuário, consulte o guia Tokens de acesso de usuário.

Para o início rápido, substitua <AUTHENTICATION_TOKEN> por um token de acesso de usuário gerado para seu recurso do Serviço de Comunicação do Azure.

Depois de ter um token, inicialize uma CallAgent instância com ele, o que nos permite fazer e receber chamadas. Para acessar as câmeras no dispositivo, também precisamos obter a instância do Gerenciador de Dispositivos.

Adicione o seguinte código à InitCallAgentAndDeviceManagerAsync função.

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;

Iniciar uma chamada com vídeo

Adicione a implementação ao CallButton_Click para iniciar uma chamada com vídeo. Precisamos enumerar as câmeras com a instância do gerenciador de dispositivos e construir LocalVideoStream. Precisamos definir o VideoOptions com LocalVideoStream e passá-lo com startCallOptions para definir as opções iniciais para a chamada. Ao anexar LocalVideoStream a um MediaPlayerElement, podemos ver a visualização do 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;

Aceitar uma chamada recebida

Adicione a implementação ao Agent_OnIncomingCallAsync para atender uma chamada recebida com vídeo, passe o LocalVideoStream para 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);

Participantes remotos e fluxos de vídeo remotos

Todos os participantes remotos estão disponíveis através da RemoteParticipants coleta em uma instância de chamada. Uma vez que a chamada está conectada, podemos acessar os participantes remotos da chamada e lidar com os fluxos de vídeo remotos.

Nota

Quando um usuário ingressa em uma chamada, ele pode acessar os participantes remotos atuais por meio da RemoteParticipants coleção. O OnRemoteParticipantsUpdated evento não será acionado para esses participantes existentes. Esse evento só será acionado quando um participante remoto entrar ou sair da chamada enquanto o usuário já estiver na chamada.

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

Renderizar vídeos remotos

Para cada fluxo de vídeo remoto, anexe-o MediaPlayerElementao .

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

Atualização do estado da chamada

Precisamos limpar os renderizadores de vídeo assim que a chamada for desconectada e lidar com o caso quando os participantes remotos entrarem inicialmente na chamada.

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

Terminar uma chamada

Termine a chamada atual quando o Hang Up botão for clicado. Adicione a implementação ao HangupButton_Click para encerrar uma chamada com o callAgent que criamos e destrua os manipuladores de eventos de atualização e estado de chamada do participante.

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

Executar o código

Você pode criar e executar o código no Visual Studio. Para plataformas de solução, suportamos ARM64e x86x64 .

Você pode fazer uma chamada de vídeo de saída fornecendo um ID de usuário no campo de texto e clicando no Start Call botão.

Nota: A chamada 8:echo123 interrompe o fluxo de vídeo porque o echo bot não suporta streaming de vídeo.

Para obter mais informações sobre IDs de usuário (identidade), consulte o guia de Tokens de Acesso de Usuário.