Compartilhar via


Começar com o exemplo de destaque de chamada

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

Neste início rápido de exemplo, aprendemos como o exemplo funciona antes de o executarmos em seu computador local e, em seguida, implantamos o exemplo no Azure usando seus próprios recursos de Serviços de Comunicação do Azure.

Código de download

Localize o projeto para este exemplo no GitHub. No momento, uma versão do exemplo que inclui recursos, como a Interoperabilidade do Teams e a Gravação de Chamada está em visualização pública e pode ser localizada em um branch separado.

Implantar no Azure

Visã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. Esse aplicativo envia solicitações a um aplicativo do lado do servidor ASP.NET Core que ajuda o aplicativo do lado do cliente a se conectar ao Azure.

Aqui está um exemplo:

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

Quando você pressiona o botão "Iniciar uma chamada", o aplicativo Web busca um token de acesso do 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 é recuperado, você é solicitado a especificar a câmera e o microfone que deseja usar. Você pode desabilitar/habilitar seus dispositivos com controles de alternância:

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

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

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

Componentes da tela principal de chamada:

  • Galeria de Mídia: a fase principal em que os participantes são mostrados. Se um participante tiver a câmera habilitada, o feed de vídeo dele será mostrado aqui. Cada participante tem um bloco individual que mostra seu nome de exibição e fluxo de vídeo (quando há um)
  • Cabeçalho: É aí que os controles de chamada primários estão localizados para alternar as configurações e a barra lateral do participante, ativar/desativar vídeo e combinação, compartilhar a tela e sair da chamada.
  • Barra lateral: é nela que as informações de participantes e configurações são mostradas quando alternadas 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 chat. A barra lateral de configurações permite que você defina as configurações de microfone e câmera.

Confira abaixo mais informações sobre os pré-requisitos e as etapas para configurar o exemplo.

Pré-requisitos

Antes de executar o exemplo 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 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, confira Criar Recursos de Comunicação do Azure

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

  4. Obtenha 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 pontos de extremidade, confira Criar Recursos de Comunicação do Azure

  5. Depois de obter o Endpoint String, adicione a cadeia do ponto de extremidade ao arquivo samples/Server/appsetting.json. Insira a cadeia de conexão na variável: EndpointUrl

Execução local

  1. Instalar dependências

    npm run setup
    
  2. Inicie o aplicativo da chamada

    npm run start
    

    Isso abre um servidor cliente na porta 3000 que atende aos arquivos do site e um servidor de API na porta 8080 que executa funcionalidades como tokens de mineração para participantes da chamada.

Solução de problemas

  • O aplicativo mostra uma tela "Navegador sem suporte", mas estou em um navegador com suporte.

    Se o aplicativo estiver sendo servido em um nome de host diferente do localhost, você deverá atender ao tráfego por https e não por 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 De chamada/dist em seu serviço de aplicativo

Limpar os recursos

Se quiser limpar e remover uma assinatura dos Serviços de Comunicação, exclua o recurso ou o grupo de recursos. Excluir o grupo de recursos também exclui todos os recursos associados a ele. Saiba mais sobre como limpar recursos.

Próximas etapas

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

Leituras adicionais

  • Amostras – encontre mais amostras e exemplos em nossa página de visão geral de amostras.
  • Redux – gerenciamento de estado do lado do cliente
  • FluentUI – biblioteca de interface do usuário da Microsoft
  • React – biblioteca para criar interfaces do usuário
  • ASP.NET Core – Estrutura para criar aplicativos Web

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

Código de download

Localize o projeto para este exemplo no GitHub.

Visão geral

O exemplo é um aplicativo iOS nativo que usa os SDKs para iOS dos Serviços de Comunicação do Azure para criar uma experiência de chamada com 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 o Azure Functions.

Aqui está um exemplo:

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

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

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

Depois de tocar em "Avançar" na tela "Iniciar chamada", você tem a oportunidade de compartilhar a ID do grupo da chamada por meio da planilha de compartilhamento do iOS.

Captura da tela “Compartilhar ID do grupo” 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 a ID da chamada existente ou o link da ID do Teams.

Captura da tela “Ingressar na chamada” do aplicativo de exemplo.

Depois de ingressar em uma chamada, você precisará fornecer permissão ao aplicativo para acessar sua câmera e seu microfone, se ainda não tiver feito isso. Tenha em mente que, assim como em todos os aplicativos baseados em AVFoundation, a verdadeira funcionalidade de áudio e vídeo só está disponível no hardware real.

Depois de configurar seu nome de exibição e ingressar na chamada, você verá a tela de chamada principal que hospeda a experiência de chamada principal.

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

Componentes da tela principal de chamada:

  • Galeria de Mídia: a fase principal em que os participantes são mostrados. Se um participante tiver a câmera habilitada, o feed de vídeo dele será mostrado aqui. Cada participante tem um bloco individual que mostra o nome de exibição e o fluxo de vídeo (quando houver). A galeria dá suporte a vários participantes e é atualizada quando os participantes são adicionados à chamada ou removidos dela.
  • Barra de Ação: é nela que os controles de chamada primários estão localizados. Esses controles permitem ativar/desativar o vídeo e o microfone, compartilhar a tela e sair da chamada.

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

Pré-requisitos

Como executar o exemplo localmente

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

Antes de executar o exemplo pela primeira vez

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

Executar o exemplo

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

(Opcional) Como proteger um ponto de extremidade de autenticação

Para fins de demonstração, esse exemplo usa um ponto de extremidade publicamente acessível 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 um ponto de extremidade próprio protegido para provisionar seus tokens.

Com uma configuração adicional, este exemplo dá suporte à conexão com um ponto de extremidade protegido da ID do Microsoft Entra (Microsoft Entra ID) 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. Confira as etapas 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. Anote a Application (client) ID, a Directory (tenant) ID e o Application ID URI

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

  1. Crie um arquivo AppSettings.xcconfig na raiz, se ele 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>
    

Limpar os recursos

Se quiser limpar e remover uma assinatura dos Serviços de Comunicação, exclua o recurso ou o grupo de recursos. Excluir o grupo de recursos também exclui todos os recursos associados a ele. Saiba mais sobre como limpar recursos.

Próximas etapas

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

Leituras adicionais

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

Código de download

Localize o projeto para este exemplo no GitHub.

Visão geral

O exemplo é um aplicativo Android nativo que usa a biblioteca de clientes da IU do Android dos Serviços de Comunicação do Azure para criar uma experiência de chamada com 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 o Azure Functions.

Aqui está um exemplo:

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

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

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

Depois de tocar em "Avançar" na página "Iniciar chamada", você tem a oportunidade de compartilhar a "ID da chamada de grupo".

Captura da tela “Compartilhar ID da chamada de grupo” 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 dessa chamada ou o link da ID da reunião do Teams e o nome de exibição.

Captura da tela “Ingressar na chamada” do aplicativo de exemplo.

Depois de ingressar em uma chamada, você precisará fornecer permissão ao aplicativo para acessar sua câmera e seu microfone, se ainda não tiver feito isso. Você verá a tela principal de chamada na qual está a experiência de chamada principal.

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

Componentes da tela principal de chamada:

  • Galeria de Mídia: a fase principal em que os participantes são mostrados. Se um participante tiver a câmera habilitada, o feed de vídeo dele será mostrado aqui. Cada participante tem um bloco individual que mostra o nome de exibição e o fluxo de vídeo (quando houver). A galeria dá suporte a vários participantes e é atualizada quando os participantes são adicionados à chamada ou removidos dela.
  • Barra de Ação: é nela que os controles de chamada primários estão localizados. Esses controles permitem ativar/desativar o vídeo e o microfone, compartilhar a tela e sair da chamada.

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

Pré-requisitos

Como executar o exemplo localmente

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

Antes de executar o exemplo pela primeira vez

  1. Abra o Android Studio e selecione Open an Existing Project
  2. Abra a pasta AzureCalling dentro da versão baixada do exemplo.
  3. Expanda app/assets para atualizar appSettings.properties. Defina o valor da chave communicationTokenFetchUrl como a URL para o ponto de extremidade de autenticação com um pré-requisito.

Executar o exemplo

Compile e execute o exemplo no Android Studio.

(Opcional) Como proteger um ponto de extremidade de autenticação

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

Com uma configuração adicional, este exemplo dá suporte à conexão a um ponto de extremidade protegido da ID do Microsoft Entra (Microsoft Entra ID) 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. Confira as etapas 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. Anote o Package name, Signature hash, MSAL Configuration.

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 habilitar o Microsoft Entra ID.

  2. Edite AndroidManifest.xml e defina android:path como o hash de assinatura do repositório de chaves. (Opcional. O valor atual usa hash do pacote debug.keystore. Se for usado um repositório de chaves diferente, ele 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 da MSAL Android do portal do Azure e cole-a em AzureCalling/app/src/main/res/raw/auth_config_single_account.json. Inclua "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 como a URL para o ponto de extremidade de autenticação seguro.

  5. Edite AzureCalling/app/src/main/res/raw/auth_config_single_account.json e defina o valor da chave aadScopes dos escopos Azure Active Directory Expose an API

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

  7. Edite AzureCalling/app/src/main/assets/appSettings.properties e defina o valor da chave tenant a fim de habilitar o logon silencioso para que o usuário não precise ser autenticado várias vezes durante a reinicialização do aplicativo.

Limpar os recursos

Se quiser limpar e remover uma assinatura dos Serviços de Comunicação, exclua o recurso ou o grupo de recursos. Excluir o grupo de recursos também exclui todos os recursos associados a ele. Saiba mais sobre como limpar recursos.

Próximas etapas

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

Leituras adicionais

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

Neste guia de início rápido, você aprenderá a iniciar uma chamada de vídeo individual 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, você precisará dos seguintes pré-requisitos:

Configurando

Criação do projeto

No Visual Studio, crie um novo projeto com o modelo Aplicativo em branco (Universal do Windows) para configurar um aplicativo UWP (Plataforma Universal do Windows 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 acesse Manage Nuget Packages para instalar Azure.Communication.Calling.WindowsClient 1.2.0-beta.1 ou versão superior. Certifique-se de que Incluir Pré-lançamento esteja marcado.

Solicitar acesso

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

Adicione o código a seguir ao seu Package.appxmanifest clicando com o botão direito do mouse e escolhendo Exibir código.

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

Configurar o framework de aplicativos

Precisamos configurar um layout básico para anexar nossa lógica. Para fazer uma chamada de saída, precisamos de um TextBox para fornecer a ID de usuário do receptor. Também precisamos de um botão Start Call e de um botão Hang Up. 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 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 o App.xaml.cs (clique com o botão direito do mouse e escolha Exibir código) e adicione essa linha à parte superior:

using CallingQuickstart;

Abra o MainPage.xaml.cs (clique com o botão direito do mouse e escolha Exibir código) 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 cuidam de alguns dos principais recursos do SDK de Chamada da Interface do Usuário dos Serviços de Comunicação do Azure:

Nome Descrição
CallClient O CallClient é o ponto de entrada principal para a biblioteca de clientes de Chamada.
CallAgent O CallAgent é usado para iniciar e ingressar em chamadas.
CommunicationCall O CommunicationCall é usado para gerenciar chamadas iniciadas ou ingressadas.
CallTokenCredential O CallTokenCredential é usado como a credencial de token de segurança para criar uma instância 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ê precisará de um Token de Acesso de Usuário. Geralmente, esse token é gerado de um serviço com autenticação específica para o aplicativo. Para saber mais sobre tokens de acesso do usuário, confira o guia Tokens de acesso do usuário.

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

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

Adicione o seguinte código à função 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
    }
}

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 e o constructo do gerenciador de dispositivos LocalOutgoingVideoStream. Precisamos definir o VideoOptions com LocalVideoStream e passá-lo com startCallOptions para definir as opções iniciais da chamada. Ao anexar LocalOutgoingVideoStream a um MediaElement, podemos visualizar o 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 (Serviços de Comunicação do Azure 1:1, chamada telefônica 1:1, chamada de grupo de Serviços de Comunicação do Azure, ingresso na reunião do Teams, etc.).

private async Task<CommunicationCall> StartAcsCallAsync(string acsCallee)
{
    var options = await GetStartCallOptionsAsync();
    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 GetStartCallOptionsAsync();
    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> GetStartCallOptionsAsync()
{
    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 método CameraList_SelectionChanged.

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

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

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

Participante remoto e fluxos de vídeo remotos

Todos os participantes remotos estão disponíveis por meio da coleção RemoteParticipants em uma instância de chamada. Assim que a chamada é conectada (CallState.Connected), podemos acessar os participantes remotos da chamada e lidar com os streamings de vídeo remotos.

Observação

Quando um usuário entra em uma chamada, ele pode acessar os participantes remotos atuais por meio da coleção RemoteParticipants. O evento RemoteParticipantsUpdated 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 ao MediaElement.

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

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

Atualização do estado de chamada

Precisamos limpar os renderizadores de vídeo depois que a chamada for desconectada e tratar o caso quando os participantes remotos ingressarem 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;
    }
}

Encerrar uma chamada

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

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, oferecemos suporte a ARM64, x64 e x86.

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

Observação: chamar 8:echo123 interromperá o fluxo de vídeo porque o bot de eco não dá suporte ao streaming de vídeo.

Para obter mais informações sobre IDs (identidade) de usuário, verifique o guia Tokens de acesso do usuário.

Código de exemplo do WinUI 3

Pré-requisitos

Para concluir este tutorial, você precisará dos seguintes pré-requisitos:

Configurando

Criação do projeto

No Visual Studio, crie um novo projeto com o modelo Blank App, Packaged (WinUI 3 in Desktop) 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 acesse Manage Nuget Packages para instalar Azure.Communication.Calling.WindowsClient 1.0.0 ou versão superior. Certifique-se de que Incluir Pré-lançamento esteja marcado.

Solicitar acesso

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

Adicione o seguinte código à sua app.manifest:

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

Configurar o framework de aplicativos

Precisamos configurar um layout básico para anexar nossa lógica. Para fazer uma chamada de saída, precisamos de um TextBox para fornecer a ID de usuário do receptor. Também precisamos de um botão Start Call e de um botão Hang Up. 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 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 o App.xaml.cs (clique com o botão direito do mouse e escolha Exibir código) e adicione essa linha à parte superior:

using CallingQuickstart;

Abra o MainWindow.xaml.cs (clique com o botão direito do mouse e escolha Exibir código) 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 cuidam de alguns dos principais recursos do SDK de Chamada da Interface do Usuário dos Serviços de Comunicação do Azure:

Nome Descrição
CallClient O CallClient é o ponto de entrada principal para a biblioteca de clientes de Chamada.
CallAgent O CallAgent é usado para iniciar e ingressar em chamadas.
CommunicationCall O CommunicationCall é usado para gerenciar chamadas iniciadas ou ingressadas.
CallTokenCredential O CallTokenCredential é usado como a credencial de token de segurança para criar uma instância 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ê precisará de um Token de Acesso de Usuário. Geralmente, esse token é gerado de um serviço com autenticação específica para o aplicativo. Para saber mais sobre tokens de acesso do usuário, confira o guia Tokens de acesso do usuário.

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

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

Adicione o seguinte código à função 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;

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 e o constructo do gerenciador de dispositivos LocalVideoStream. Precisamos definir o VideoOptions com LocalVideoStream e passá-lo com startCallOptions para definir as opções iniciais da chamada. Ao anexar LocalVideoStream a um MediaPlayerElement, podemos visualizar o vídeo local.

var startCallOptions = new StartCallOptions();

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

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

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 selectedCamera = CameraList.SelectedItem as VideoDeviceDetails;
        cameraStream = new LocalOutgoingVideoStream(selectedCamera);

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

Participante remoto e fluxos de vídeo remotos

Todos os participantes remotos estão disponíveis por meio da coleção RemoteParticipants em uma instância de chamada. Quando a chamada estiver conectada, podemos acessar os participantes remotos da chamada e identificar os fluxos de vídeo remotos.

Observação

Quando um usuário entra em uma chamada, ele pode acessar os participantes remotos atuais por meio da coleção RemoteParticipants. O evento OnRemoteParticipantsUpdated 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 ao MediaPlayerElement.

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

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

Atualização do estado de chamada

Precisamos limpar os renderizadores de vídeo depois que a chamada for desconectada e tratar o caso quando os participantes remotos ingressarem 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;
    }
}

Encerrar uma chamada

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

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, oferecemos suporte a ARM64, x64 e x86.

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

Observação: chamar 8:echo123 interromperá o fluxo de vídeo porque o bot de eco não dá suporte ao streaming de vídeo.

Para obter mais informações sobre IDs (identidade) de usuário, verifique o guia Tokens de acesso do usuário.