Introdução ao exemplo de herói chamador

Warning

O Azure Communication Services está a introduzir alterações urgentes, e alguns serviços estão a ser retirados. Saiba mais no guia de reforma e mudanças urgentes.

O exemplo Hero para chamadas em grupo dos Serviços de Comunicação do Azure demonstra como o SDK Web de Chamadas dos Serviços de Comunicação pode ser utilizado para criar uma experiência de chamadas 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 funcionalidades atualmente em pré-visualização pública, como Teams Interop e Gravação de chamadas, pode ser encontrada num ramo separado.

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. Verá o ecrã principal da chamada, onde decorre a experiência principal de chamadas.

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 se encontram os principais controlos da chamada para alternar as definições e a barra lateral dos participantes, ativar e desativar o vídeo e o microfone, partilhar o ecrã e sair da chamada.
  • Barra lateral: é onde são apresentadas as informações sobre os participantes e as definições quando são ativadas ou desativadas através dos controlos 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 Connection String, adicione a cadeia de ligação a 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 String, adicione a string do endpoint ao ficheiro samples/Server/appsetting.json. Introduza a sua string de endpoint 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

  • Amostras - Encontre mais amostras e exemplos na nossa página de visão geral das amostras.
  • 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

A amostra principal dos Serviços de Comunicação do Azure Group Calling Hero Sample for iOS demonstra como o SDK de Chamadas dos Serviços de Comunicação para iOS pode ser utilizado para criar uma experiência de chamadas 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 este componente do servidor, esteja à vontade para seguir o tutorial Serviço Fidedigno 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 identificador do grupo da chamada através do painel 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 o seu nome de apresentação e entrar na chamada, verá o ecrã principal da chamada, onde decorre a experiência principal da 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 múltiplos participantes e é atualizada quando são adicionados ou removidos participantes da 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 e um certificado de desenvolvedor válido instalado no seu Keychain.
  • 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 a executar o Ponto final de autenticação para obter tokens de acesso.

Executar o exemplo 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 as dependências ao executar pod install.
  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) Proteger um ponto terminal 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 permite ligar-se a um endpoint protegido pelo Microsoft Entra ID (Microsoft Entra ID), de modo que seja necessário o início de sessão do utilizador para a aplicação obter 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. Aceda à página de descrição geral da aplicação registada em Registos de aplicações 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 Group Calling Hero Sample for Android do Azure Communication Services demonstra como o SDK Calling do Azure Communication Services para Android pode ser utilizado para criar uma experiência de chamadas 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 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 este componente do servidor, esteja à vontade para seguir o tutorial Serviço Fidedigno 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. Verá o ecrã principal da chamada, onde se encontram as funcionalidades principais 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 múltiplos participantes e é atualizada quando são adicionados ou removidos participantes da 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

Executar o exemplo 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 AzureCalling dentro da versão transferida do exemplo.
  3. Expanda app/assets para atualizar appSettings.properties. Defina o valor da chave communicationTokenFetchUrl como a URL do seu endpoint de autenticação configurado previamente.

Executar amostra

Crie e execute o exemplo no Android Studio.

(Opcional) Proteger um ponto terminal de autenticação

Para efeitos de demonstração, este exemplo utiliza, por predefinição, um ponto final acessível publicamente para obter um token do Azure Communication Services. 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 suporta a ligação a um ponto final protegido pelo Microsoft Entra ID (Microsoft Entra ID), pelo que é necessário que o utilizador inicie sessão para que a aplicação obtenha 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. Aceda à página de descrição geral da aplicação registada em Registos de aplicações do Microsoft Entra. Tome nota do 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 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 o hash do ficheiro debug.keystore incluído. Se for usado um repositório de chaves diferente, isto 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. Edite AzureCalling/app/src/main/res/raw/auth_config_single_account.json e defina o valor da chave aadScopes nos escopos Azure Active DirectoryExpose an API

  6. Defina o valor de graphURL em AzureCalling/app/assets/appSettings.properties como o endpoint da API Graph para obter informações do utilizador.

  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

A aplicação de exemplo de chamadas em grupo dos Serviços de Comunicação do Azure para Windows demonstra como o SDK de Chamadas dos Serviços de Comunicação para Windows pode ser utilizado para criar uma experiência de chamadas 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 Aplicação em Branco (Universal Windows) para criar uma aplicação 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 rato no seu projeto e vá a Manage Nuget Packages para instalar Azure.Communication.Calling.WindowsClienta versão 1.2.0-beta.1 ou posterior. Certifique-se de que a opção Incluir versões de pré-lançamento está selecionada.

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 aceder à transmissão 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 MainPage.xaml.cs (clique com o botão direito e escolha Ver 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 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 Calling.
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 do utilizador, consulte o guia Tokens de Acesso do Utilizador.

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 ligar LocalOutgoingVideoStream a um MediaElement, podemos ver a pré-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 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 CameraList_SelectionChanged método.

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 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 coleção RemoteParticipants numa 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 a 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 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

Terminar a chamada atual quando se clicar no botão Hang Up. 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ções, suportamos ARM64, x64 e x86.

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 mais informações sobre os IDs de utilizador (identidade), consulte o guia Tokens de Acesso do Utilizador.

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 Aplicação em Branco, Empacotada (WinUI 3 no Desktop) para criar uma aplicação WinUI 3 de página única.

Captura de ecrã que mostra a janela Novo Projeto WinUI no Visual Studio.

Instalar o pacote

Clique com o botão direito no seu projeto e vá a Manage Nuget Packages para instalar Azure.Communication.Calling.WindowsClient1.0.0 ou uma versão superior. Certifique-se de que a opção Incluir versões de pré-lançamento está selecionada.

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 MainWindow.xaml.cs (clique com o botão direito e escolha Ver 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 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 Calling.
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 do utilizador, consulte o guia Tokens de Acesso do Utilizador.

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 ligar LocalVideoStream a um MediaPlayerElement, podemos ver a pré-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 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 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 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);

Participantes remotos e fluxos de vídeo remotos

Todos os participantes remotos estão disponíveis através da coleção RemoteParticipants numa 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 a 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 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

Terminar a chamada atual quando se clicar no botão Hang Up. 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ções, suportamos ARM64, x64 e x86.

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 mais informações sobre os IDs de utilizador (identidade), consulte o guia Tokens de Acesso do Utilizador.