Wprowadzenie do przykładu elementu hero wywołującego

Przykład wywołania elementu Hero w usługach Azure Communication Services pokazuje, jak można użyć usług komunikacyjnych wywołujących internetowy zestaw SDK do utworzenia środowiska wywoływania grup.

W tym przykładowym przewodniku Szybki start dowiesz się, jak działa przykład przed uruchomieniem przykładu na maszynie lokalnej, a następnie wdrożymy przykład na platformie Azure przy użyciu własnych zasobów usług Azure Communication Services.

Pobieranie kodu

Znajdź projekt dla tego przykładu w witrynie GitHub. Wersja przykładu, która zawiera funkcje obecnie w publicznej wersji zapoznawczej, takie jak Teams Interop i Nagrywanie połączeń, można znaleźć w oddzielnej gałęzi.

Wdróż na platformie Azure

Omówienie

Przykład zawiera zarówno aplikację po stronie klienta, jak i aplikację po stronie serwera. Aplikacja po stronie klienta to aplikacja internetowa React/Redux, która korzysta z platformy Fluent UI firmy Microsoft. Ta aplikacja wysyła żądania do aplikacji po stronie serwera ASP.NET Core, która pomaga aplikacji po stronie klienta nawiązywać połączenie z platformą Azure.

Oto jak wygląda przykład:

Zrzut ekranu przedstawiający stronę docelową przykładowej aplikacji.

Po naciśnięciu przycisku "Uruchom wywołanie" aplikacja internetowa pobiera token dostępu użytkownika z aplikacji po stronie serwera. Ten token jest następnie używany do łączenia aplikacji klienckiej z usługami Azure Communication Services. Po pobraniu tokenu zostanie wyświetlony monit o określenie aparatu i mikrofonu, którego chcesz użyć. Możesz wyłączyć/włączyć urządzenia za pomocą kontrolek przełącznika:

Zrzut ekranu przedstawiający ekran wstępnej wywołania przykładowej aplikacji.

Po skonfigurowaniu nazwy wyświetlanej i urządzeń możesz dołączyć do sesji wywołania. Zobaczysz główną kanwę wywołań, na której znajduje się podstawowe środowisko wywołujące.

Zrzut ekranu przedstawiający ekran główny przykładowej aplikacji.

Składniki głównego ekranu wywołującego:

  • Galeria multimediów: główny etap, na którym są pokazywani uczestnicy. Jeśli uczestnik ma włączoną kamerę, ich kanał wideo jest wyświetlany tutaj. Każdy uczestnik ma pojedynczy kafelek, który pokazuje nazwę wyświetlaną i strumień wideo (jeśli istnieje)
  • Nagłówek: w tym miejscu znajdują się podstawowe kontrolki wywołań, aby przełączać ustawienia i pasek boczny uczestnika, włączać i mieszać wideo i włączać/wyłączać, ekran udostępniania i opuszczać połączenie.
  • Pasek boczny: jest to miejsce, w którym uczestnicy i informacje o ustawieniach są wyświetlane po przełączeniu za pomocą kontrolek w nagłówku. Składnik można odrzucić przy użyciu "X" w prawym górnym rogu. Pasek boczny uczestników zawiera listę uczestników i link umożliwiający zaproszenie większej liczby użytkowników do czatu. Ustawienia pasek boczny umożliwia skonfigurowanie ustawień mikrofonu i aparatu.

Poniżej znajdziesz więcej informacji na temat wymagań wstępnych i kroków konfigurowania przykładu.

Wymagania wstępne

Przed uruchomieniem przykładu po raz pierwszy

  1. Otwórz wystąpienie programu PowerShell, Terminal Windows, wiersza polecenia lub równoważnego i przejdź do katalogu, do którego chcesz sklonować przykład.

    git clone https://github.com/Azure-Samples/communication-services-web-calling-hero.git
    
  2. Pobierz z Connection String witryny Azure Portal lub przy użyciu interfejsu wiersza polecenia platformy Azure.

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

    Aby uzyskać więcej informacji na temat parametry połączenia, zobacz Create an Azure Communication Resources (Tworzenie zasobów komunikacji platformy Azure)

  3. Po pobraniu Connection Stringpliku dodaj parametry połączenia do pliku samples/Server/appsetting.json. Wprowadź parametry połączenia w zmiennej: ResourceConnectionString.

  4. Pobierz z Endpoint string witryny Azure Portal lub przy użyciu interfejsu wiersza polecenia platformy Azure.

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

    Aby uzyskać więcej informacji na temat ciągów punktów końcowych, zobacz Create an Azure Communication Resources (Tworzenie zasobów komunikacji platformy Azure)

  5. Po pobraniu pliku dodaj ciąg punktu końcowego Endpoint Stringdo pliku samples/Server/appsetting.json . Wprowadzanie ciągu punktu końcowego w zmiennej EndpointUrl

Uruchamianie lokalne

  1. Instalowanie zależności

    npm run setup
    
  2. Uruchamianie aplikacji wywołującej

    npm run start
    

    Spowoduje to otwarcie serwera klienta na porcie 3000, który obsługuje pliki witryny internetowej, oraz serwer interfejsu API na porcie 8080, który wykonuje funkcje, takie jak wybicie tokenów dla uczestników połączeń.

Rozwiązywanie problemów

  • Aplikacja wyświetla ekran "Nieobsługiwana przeglądarka", ale jestem w obsługiwanej przeglądarce.

    Jeśli aplikacja jest obsługiwana za pośrednictwem nazwy hosta innej niż localhost, musisz obsługiwać ruch za pośrednictwem protokołu HTTPS, a nie http.

Publikowanie na platformie Azure

  1. npm run setup
  2. npm run build
  3. npm run package
  4. Użyj rozszerzenia platformy Azure i wdróż katalog Calling/dist w usłudze app Service

Czyszczenie zasobów

Jeśli chcesz wyczyścić i usunąć subskrypcję usług Komunikacyjnych, możesz usunąć zasób lub grupę zasobów. Usunięcie grupy zasobów powoduje również usunięcie wszelkich innych skojarzonych z nią zasobów. Dowiedz się więcej o czyszczeniu zasobów.

Następne kroki

Aby uzyskać więcej informacji, zobacz następujące artykuły:

Materiały uzupełniające

  • Przykłady — znajdź więcej przykładów i przykładów na naszej stronie przeglądu przykładów.
  • Redux — zarządzanie stanem po stronie klienta
  • FluentUI — biblioteka interfejsu użytkownika obsługiwana przez firmę Microsoft
  • React — biblioteka do tworzenia interfejsów użytkownika
  • ASP.NET Core — struktura do tworzenia aplikacji internetowych

Przykład wywołania hero w usługach Azure Communication Services dla systemu iOS pokazuje, w jaki sposób można użyć usług komunikacyjnych wywołujących zestaw SDK systemu iOS do tworzenia środowiska wywoływania grup obejmującego głos i wideo. W tym przykładowym przewodniku Szybki start dowiesz się, jak skonfigurować i uruchomić przykład. Omówienie przykładu jest dostępne dla kontekstu.

Pobieranie kodu

Znajdź projekt dla tego przykładu w witrynie GitHub.

Omówienie

Przykład jest natywną aplikacją dla systemu iOS, która używa zestawów SDK usług Azure Communication Services dla systemu iOS do tworzenia środowiska wywołującego, które oferuje zarówno połączenie głosowe, jak i wideo. Aplikacja używa składnika po stronie serwera do aprowizowania tokenów dostępu, które są następnie używane do inicjowania zestawu SDK usług Azure Communication Services. Aby skonfigurować ten składnik po stronie serwera, możesz skorzystać z samouczka Trusted Service with Azure Functions (Zaufana usługa w usłudze Azure Functions ).

Oto jak wygląda przykład:

Zrzut ekranu przedstawiający stronę docelową przykładowej aplikacji.

Po naciśnięciu przycisku "Uruchom nowe wywołanie" aplikacja systemu iOS wyświetli monit o wprowadzenie nazwy wyświetlanej do użycia dla wywołania.

Zrzut ekranu przedstawiający ekran wstępnej wywołania przykładowej aplikacji.

Po naciśnięciu przycisku "Dalej" na ekranie "Rozpocznij połączenie" możesz udostępnić identyfikator grupy wywołania za pośrednictwem arkusza udziału systemu iOS.

Zrzut ekranu przedstawiający ekran identyfikatora grupy udziału przykładowej aplikacji.

Aplikacja umożliwia również dołączenie istniejącego wywołania usług Azure Communication Services przez określenie identyfikatora istniejącego wywołania lub linku identyfikatora zespołu.

Zrzut ekranu przedstawiający ekran wywołania sprzężenia przykładowej aplikacji.

Po dołączeniu do połączenia zostanie wyświetlony monit o udzielenie aplikacji uprawnień dostępu do aparatu i mikrofonu, jeśli jeszcze nie został autoryzowany. Należy pamiętać, że podobnie jak wszystkie aplikacje oparte na avFoundation, prawdziwe funkcje audio i wideo są dostępne tylko na rzeczywistym sprzęcie.

Po skonfigurowaniu nazwy wyświetlanej i dołączeniu do wywołania zobaczysz główną kanwę wywołania, na której znajduje się podstawowe środowisko wywołania.

Zrzut ekranu przedstawiający ekran główny przykładowej aplikacji.

Składniki głównego ekranu wywołującego:

  • Galeria multimediów: główny etap, na którym są pokazywani uczestnicy. Jeśli uczestnik ma włączoną kamerę, ich kanał wideo jest wyświetlany tutaj. Każdy uczestnik ma pojedynczy kafelek, który pokazuje swoją nazwę wyświetlaną i strumień wideo (jeśli istnieje). Galeria obsługuje wielu uczestników i jest aktualizowana po dodaniu lub usunięciu uczestników do połączenia.
  • Pasek akcji: jest to miejsce, w którym znajdują się kontrolki wywołania podstawowego. Te kontrolki umożliwiają włączenie/wyłączenie wideo i mikrofonu, udostępnienie ekranu i opuszczenie połączenia.

Poniżej znajdziesz więcej informacji na temat wymagań wstępnych i kroków konfigurowania przykładu.

Wymagania wstępne

  • Konto platformy Azure z aktywną subskrypcją. Aby uzyskać szczegółowe informacje, zobacz Tworzenie bezpłatnego konta.
  • Komputer Mac z uruchomionym programem Xcode wraz z prawidłowym certyfikatem dewelopera zainstalowanym w pęku kluczy.
  • Zasób usług Azure Communication Services. Aby uzyskać szczegółowe informacje, zobacz Tworzenie zasobu usług Azure Communication Services.
  • Funkcja platformy Azure z uruchomionym punktem końcowym uwierzytelniania w celu pobrania tokenów dostępu.

Uruchamianie przykładu lokalnie

Przykład wywołania grupy można uruchomić lokalnie przy użyciu środowiska XCode. Deweloperzy mogą testować aplikację przy użyciu urządzenia fizycznego lub emulatora.

Przed uruchomieniem przykładu po raz pierwszy

  1. Zainstaluj zależności, uruchamiając polecenie pod install.
  2. Otwórz AzureCalling.xcworkspace plik w programie XCode.
  3. Utwórz plik tekstowy w katalogu głównym o nazwie AppSettings.xcconfig i ustaw wartość:
    communicationTokenFetchUrl = <your authentication endpoint, without the https:// component>
    

Uruchom przykład

Skompiluj i uruchom przykład w środowisku XCode przy użyciu obiektu docelowego AzureCalling w wybranym symulatorze lub urządzeniu.

(Opcjonalnie) Zabezpieczanie punktu końcowego uwierzytelniania

W celach demonstracyjnych ten przykład domyślnie używa publicznie dostępnego punktu końcowego do pobrania tokenu dostępu usług Azure Communication Services. W przypadku scenariuszy produkcyjnych zalecamy użycie własnego zabezpieczonego punktu końcowego do aprowizowania własnych tokenów.

W przypadku dodatkowej konfiguracji ten przykład obsługuje nawiązywanie połączenia z chronionym punktem końcowym microsoft Entra ID (Microsoft Entra ID ), dzięki czemu logowanie użytkownika jest wymagane do pobrania tokenu dostępu usług Azure Communication Services. Zobacz poniższe kroki:

  1. Włącz uwierzytelnianie microsoft Entra w aplikacji.
  2. Przejdź do strony przeglądu zarejestrowanej aplikacji w obszarze Rejestracje aplikacji Firmy Microsoft. Zanotuj element Application (client) ID, , Directory (tenant) IDApplication ID URI

Konfiguracja microsoft Entra w witrynie Azure Portal.

  1. AppSettings.xcconfig Utwórz plik w katalogu głównym, jeśli jeszcze nie istnieje i dodaj wartości:
    communicationTokenFetchUrl = <Application ID URI, without the https:// component>
    aadClientId = <Application (client) ID>
    aadTenantId = <Directory (tenant) ID>
    

Czyszczenie zasobów

Jeśli chcesz wyczyścić i usunąć subskrypcję usług Komunikacyjnych, możesz usunąć zasób lub grupę zasobów. Usunięcie grupy zasobów powoduje również usunięcie wszelkich innych skojarzonych z nią zasobów. Dowiedz się więcej o czyszczeniu zasobów.

Następne kroki

Aby uzyskać więcej informacji, zobacz następujące artykuły:

Materiały uzupełniające

Przykład wywołania hero w usługach Azure Communication Services dla systemu Android pokazuje, w jaki sposób można użyć usług komunikacyjnych wywołujących system Android SDK do tworzenia środowiska wywoływania grup obejmującego głos i wideo. W tym przykładowym przewodniku Szybki start dowiesz się, jak skonfigurować i uruchomić przykład. Omówienie przykładu jest dostępne dla kontekstu.

Pobieranie kodu

Znajdź projekt dla tego przykładu w witrynie GitHub.

Omówienie

Przykład jest natywną aplikacją systemu Android, która używa biblioteki klienta interfejsu użytkownika systemu Android usług Azure Communication Services do tworzenia środowiska wywołującego, które oferuje zarówno połączenie głosowe, jak i wideo. Aplikacja używa składnika po stronie serwera do aprowizowania tokenów dostępu, które są następnie używane do inicjowania zestawu SDK usług Azure Communication Services. Aby skonfigurować ten składnik po stronie serwera, możesz skorzystać z samouczka Trusted Service with Azure Functions (Zaufana usługa w usłudze Azure Functions ).

Oto jak wygląda przykład:

Zrzut ekranu przedstawiający stronę docelową przykładowej aplikacji.

Po naciśnięciu przycisku "Rozpocznij nowe wywołanie" aplikacja systemu Android wyświetli monit o wprowadzenie nazwy wyświetlanej do użycia dla wywołania.

Zrzut ekranu przedstawiający ekran wstępnej wywołania przykładowej aplikacji.

Po naciśnięciu przycisku "Dalej" na stronie "Rozpocznij połączenie" możesz udostępnić identyfikator połączenia grupowego.

Zrzut ekranu przedstawiający ekran identyfikatora wywołania grupy udziału przykładowej aplikacji.

Aplikacja umożliwia dołączenie istniejącego wywołania usług Azure Communication Services przez określenie identyfikatora istniejącego połączenia lub identyfikatora spotkania zespołów i nazwy wyświetlanej.

Zrzut ekranu przedstawiający ekran wywołania sprzężenia przykładowej aplikacji.

Po dołączeniu do połączenia zostanie wyświetlony monit o udzielenie aplikacji uprawnień dostępu do aparatu i mikrofonu, jeśli jeszcze nie został autoryzowany. Zobaczysz główną kanwę wywołań, na której znajduje się podstawowe środowisko wywołujące.

Zrzut ekranu przedstawiający ekran główny przykładowej aplikacji.

Składniki głównego ekranu wywołującego:

  • Galeria multimediów: główny etap, na którym są pokazywani uczestnicy. Jeśli uczestnik ma włączoną kamerę, ich kanał wideo jest wyświetlany tutaj. Każdy uczestnik ma pojedynczy kafelek, który pokazuje swoją nazwę wyświetlaną i strumień wideo (jeśli istnieje). Galeria obsługuje wielu uczestników i jest aktualizowana po dodaniu lub usunięciu uczestników do połączenia.
  • Pasek akcji: jest to miejsce, w którym znajdują się kontrolki wywołania podstawowego. Te kontrolki umożliwiają włączenie/wyłączenie wideo i mikrofonu, udostępnienie ekranu i opuszczenie połączenia.

Poniżej znajdziesz więcej informacji na temat wymagań wstępnych i kroków konfigurowania przykładu.

Wymagania wstępne

  • Konto platformy Azure z aktywną subskrypcją. Aby uzyskać szczegółowe informacje, zobacz Tworzenie bezpłatnego konta.
  • Program Android Studio uruchomiony na komputerze
  • Zasób usług Azure Communication Services. Aby uzyskać szczegółowe informacje, zobacz Tworzenie zasobu usług Azure Communication Services.
  • Funkcja platformy Azure z uruchomionym punktem końcowym uwierzytelniania w celu pobrania tokenów dostępu.

Uruchamianie przykładu lokalnie

Przykład wywołania grupy można uruchamiać lokalnie przy użyciu programu Android Studio. Deweloperzy mogą testować aplikację przy użyciu urządzenia fizycznego lub emulatora.

Przed uruchomieniem przykładu po raz pierwszy

  1. Otwórz program Android Studio i wybierz pozycję Open an Existing Project
  2. AzureCalling Otwórz folder w wersji pobranej dla przykładu.
  3. Rozwiń węzeł app/assets, aby zaktualizować appSettings.propertiesplik . Ustaw wartość klucza communicationTokenFetchUrl jako adres URL punktu końcowego uwierzytelniania skonfigurowanego jako wymaganie wstępne.

Uruchom przykład

Skompiluj i uruchom przykład w programie Android Studio.

(Opcjonalnie) Zabezpieczanie punktu końcowego uwierzytelniania

W celach demonstracyjnych ten przykład domyślnie używa publicznie dostępnego punktu końcowego do pobrania tokenu usług Azure Communication Services. W przypadku scenariuszy produkcyjnych zalecamy użycie własnego zabezpieczonego punktu końcowego do aprowizowania własnych tokenów.

W przypadku dodatkowej konfiguracji ten przykład obsługuje nawiązywanie połączenia z chronionym punktem końcowym microsoft Entra ID (Microsoft Entra ID ), dzięki czemu logowanie użytkownika jest wymagane do pobrania tokenu usług Azure Communication Services przez aplikację. Zobacz poniższe kroki:

  1. Włącz uwierzytelnianie microsoft Entra w aplikacji.

  2. Przejdź do strony przeglądu zarejestrowanej aplikacji w obszarze Rejestracje aplikacji Firmy Microsoft. Zanotuj wartości Package name, , Signature hashMSAL Configutaion.

Konfiguracja microsoft Entra w witrynie Azure Portal.

  1. Edytuj AzureCalling/app/src/main/res/raw/auth_config_single_account.json i ustaw, isAADAuthEnabled aby włączyć identyfikator Entra firmy Microsoft.

  2. Edytuj AndroidManifest.xml i ustaw wartość android:path skrótu sygnatury magazynu kluczy. (Opcjonalnie. Bieżąca wartość używa skrótu z pakietu debug.keystore. Jeśli jest używany inny magazyn kluczy, należy go zaktualizować).

    <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. Skopiuj konfigurację biblioteki MSAL dla systemu Android z witryny Azure Portal i wklej ją do AzureCalling/app/src/main/res/raw/auth_config_single_account.json. Uwzględnij "account_mode": "SINGLE"

       {
          "client_id": "",
          "authorization_user_agent": "DEFAULT",
          "redirect_uri": "",
          "account_mode" : "SINGLE",
          "authorities": [
             {
                "type": "AAD",
                "audience": {
                "type": "AzureADMyOrg",
                "tenant_id": ""
                }
             }
          ]
       }
    
  4. Edytuj AzureCalling/app/src/main/res/raw/auth_config_single_account.json i ustaw wartość klucza communicationTokenFetchUrl jako adres URL bezpiecznego punktu końcowego uwierzytelniania.

  5. Edytuj AzureCalling/app/src/main/res/raw/auth_config_single_account.json i ustaw wartość klucza aadScopes z Azure Active DirectoryExpose an API zakresów

  6. Ustaw wartość dla graphURL elementu w AzureCalling/app/assets/appSettings.properties polu Punkt końcowy interfejsu API programu Graph, aby pobrać informacje o użytkowniku.

  7. Edytuj AzureCalling/app/src/main/assets/appSettings.properties i ustaw wartość klucza tenant , aby włączyć logowanie dyskretne, aby użytkownik nie musiał być ponownie uwierzytelniany i ponownie podczas ponownego uruchamiania aplikacji.

Czyszczenie zasobów

Jeśli chcesz wyczyścić i usunąć subskrypcję usług Komunikacyjnych, możesz usunąć zasób lub grupę zasobów. Usunięcie grupy zasobów powoduje również usunięcie wszelkich innych skojarzonych z nią zasobów. Dowiedz się więcej o czyszczeniu zasobów.

Następne kroki

Aby uzyskać więcej informacji, zobacz następujące artykuły:

Materiały uzupełniające

Przykład wywołania hero w usługach Azure Communication Services dla systemu Windows pokazuje, jak można użyć usług komunikacyjnych wywołujących windows SDK do tworzenia środowiska wywoływania grup obejmującego głos i wideo. Z tego przykładu dowiesz się, jak skonfigurować i uruchomić przykład. Omówienie przykładu jest dostępne dla kontekstu.

Z tego przewodnika Szybki start dowiesz się, jak uruchomić połączenie wideo 1:1 przy użyciu zestawu SDK wywołującego usługi Azure Communication Services dla systemu Windows.

Przykładowy kod platformy UWP

Wymagania wstępne

Do wykonania kroków tego samouczka niezbędne jest spełnienie następujących wymagań wstępnych:

  • Konto platformy Azure z aktywną subskrypcją. Utwórz konto bezpłatnie.

  • Zainstaluj program Visual Studio 2022 z pakietem roboczym programowania platforma uniwersalna systemu Windows.

  • Wdrożony zasób usług komunikacyjnych. Utwórz zasób usług komunikacyjnych. Musisz zarejestrować parametry połączenia na potrzeby tego przewodnika Szybki start.

  • Token dostępu użytkownika dla usługi Azure Communication Service. Możesz również użyć interfejsu wiersza polecenia platformy Azure i uruchomić polecenie za pomocą parametry połączenia, aby utworzyć użytkownika i token dostępu.

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

    Aby uzyskać szczegółowe informacje, zobacz Tworzenie tokenów dostępu za pomocą interfejsu wiersza polecenia platformy Azure i zarządzanie nimi.

Konfigurowanie

Tworzenie projektu

W programie Visual Studio utwórz nowy projekt przy użyciu szablonu Pusta aplikacja (uniwersalny system Windows), aby skonfigurować jednostronicową aplikację platforma uniwersalna systemu Windows (UWP).

Zrzut ekranu przedstawiający okno Nowy projekt platformy UWP w programie Visual Studio.

Instalowanie pakietu

Kliknij prawym przyciskiem myszy projekt i przejdź do Manage Nuget Packages witryny , aby zainstalować Azure.Communication.Calling.WindowsClientwersję 1.2.0-beta.1 lub nowszą. Upewnij się, że zaznaczono opcję Uwzględnij wersję Preleased.

Żądanie dostępu

Przejdź do Package.appxmanifest strony i kliknij pozycję Capabilities. Sprawdź Internet (Client & Server) , czy uzyskać dostęp przychodzący i wychodzący do Internetu. Sprawdź Microphone , czy uzyskać dostęp do kanału audio mikrofonu. Sprawdź WebCam , czy chcesz uzyskać dostęp do aparatu urządzenia.

Dodaj następujący kod do pliku Package.appxmanifest , klikając prawym przyciskiem myszy i wybierając polecenie Wyświetl kod.

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

Konfigurowanie struktury aplikacji

Musimy skonfigurować podstawowy układ, aby dołączyć naszą logikę. Aby umieścić wywołanie wychodzące, musimy TextBox podać identyfikator użytkownika wywoływanego. Potrzebujemy Start Call również przycisku i Hang Up przycisku. Musimy również wyświetlić podgląd lokalnego wideo i renderować zdalne wideo innego uczestnika. Potrzebujemy więc dwóch elementów do wyświetlania strumieni wideo.

MainPage.xaml Otwórz projekt i zastąp zawartość następującą implementacją.

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

Otwórz do App.xaml.cs (kliknij prawym przyciskiem myszy i wybierz polecenie Wyświetl kod) i dodaj ten wiersz do góry:

using CallingQuickstart;

Otwórz (kliknij prawym przyciskiem MainPage.xaml.cs myszy i wybierz pozycję Wyświetl kod) i zastąp zawartość następującą implementacją:

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

Model obiektów

Następujące klasy i interfejsy obsługują niektóre główne funkcje zestawu AZURE Communication Services Calling SDK:

Nazwa/nazwisko opis
CallClient Jest CallClient to główny punkt wejścia do biblioteki wywołującej klienta.
CallAgent Element służy do uruchamiania CallAgent i dołączania wywołań.
CommunicationCall Służy CommunicationCall do zarządzania umieszczonymi lub sprzężonym wywołaniami.
CallTokenCredential Element CallTokenCredential jest używany jako poświadczenie tokenu w celu utworzenia wystąpienia elementu CallAgent.
CommunicationUserIdentifier Element CommunicationUserIdentifier służy do reprezentowania tożsamości użytkownika, która może być jedną z następujących opcji: CommunicationUserIdentifier, PhoneNumberIdentifier lub CallingApplication.

Uwierzytelnianie użytkownika

Aby zainicjować element CallAgent, potrzebujesz tokenu dostępu użytkownika. Zazwyczaj ten token jest generowany na podstawie usługi z uwierzytelnianiem specyficznym dla aplikacji. Aby uzyskać więcej informacji na temat tokenów dostępu użytkowników, zapoznaj się z przewodnikiem Tokeny dostępu użytkowników.

W tym przewodniku Szybki start zastąp <AUTHENTICATION_TOKEN> ciąg tokenem dostępu użytkownika wygenerowanym dla zasobu usługi Azure Communication Service.

Po utworzeniu tokenu zainicjuj CallAgent wystąpienie, co umożliwi nam wykonywanie i odbieranie wywołań. Aby uzyskać dostęp do kamer na urządzeniu, musimy również uzyskać Menedżer urządzeń wystąpienie.

Dodaj następujący kod do InitCallAgentAndDeviceManagerAsync funkcji .

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

Rozpoczynanie połączenia za pomocą wideo

Dodaj implementację do CallButton_Click elementu , aby rozpocząć wywołanie za pomocą wideo. Musimy wyliczyć aparaty za pomocą wystąpienia menedżera urządzeń i skonstruować LocalOutgoingVideoStreamelement . Musimy ustawić VideoOptions element i LocalVideoStream przekazać go za startCallOptions pomocą , aby ustawić początkowe opcje dla wywołania. Dołączając LocalOutgoingVideoStream do pliku MediaElement, możemy zobaczyć podgląd lokalnego wideo.

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

Dodaj metody uruchamiania lub dołączania do różnych typów połączeń (połączenie z usługami Azure Communication Services 1:1, połączenie telefoniczne 1:1, połączenie grupy usług Azure Communication Services, dołączanie do spotkania w usłudze Teams itp.).

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

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

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

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

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

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

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

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

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

Dodaj kod, aby utworzyć element LocalVideoStream w zależności od wybranej kamery w metodzie CameraList_SelectionChanged .

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

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

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

Akceptowanie połączenia przychodzącego

Dodaj implementację do OnIncomingCallAsync elementu , aby odpowiedzieć na połączenie przychodzące za pomocą wideo, przekaż element do acceptCallOptions.LocalVideoStream

var incomingCall = args.IncomingCall;

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

_ = await incomingCall.AcceptAsync(acceptCallOptions);

Zdalny uczestnik i zdalne strumienie wideo

Wszyscy uczestnicy zdalni są dostępni za pośrednictwem kolekcji w wystąpieniu RemoteParticipants wywołania. Po nawiązaniu połączenia możemy uzyskać dostęp do zdalnych uczestników połączenia i obsłużyć zdalne strumienie wideo.


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

}

Renderowanie zdalnych filmów wideo

Dla każdego zdalnego strumienia wideo dołącz go do .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();
        });
    }
}

Aktualizacja stanu wywołania

Musimy wyczyścić programy renderujących wideo po rozłączeniu wywołania i obsłużyć przypadek, gdy uczestnicy zdalni początkowo dołączą do wywołania.

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

Kończ połączenie

Zakończ bieżące wywołanie po kliknięciu Hang Up przycisku. Dodaj implementację do HangupButton_Click, aby zakończyć wywołanie utworzonego przez nas agenta callAgent i usunąć aktualizację uczestnika i wywołać programy obsługi zdarzeń stanu.

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

Uruchamianie kodu

Możesz skompilować i uruchomić kod w programie Visual Studio. W przypadku platform rozwiązań obsługujemy ARM64platformy , x64 i x86.

Możesz wykonać wychodzące połączenie wideo, podając identyfikator użytkownika w polu tekstowym i klikając Start Call przycisk.

Uwaga: wywołanie 8:echo123 zatrzymuje strumień wideo, ponieważ bot echo nie obsługuje przesyłania strumieniowego wideo.

Aby uzyskać więcej informacji na temat identyfikatorów użytkowników (tożsamości), zapoznaj się z przewodnikiem Tokeny dostępu użytkowników.

Przykładowy kod WinUI 3

Wymagania wstępne

Do wykonania kroków tego samouczka niezbędne jest spełnienie następujących wymagań wstępnych:

Konfigurowanie

Tworzenie projektu

W programie Visual Studio utwórz nowy projekt przy użyciu szablonu Blank App, Packaged (WinUI 3 in Desktop), aby skonfigurować jednostronicową aplikację WinUI 3.

Zrzut ekranu przedstawiający okno Nowy projekt WinUI w programie Visual Studio.

Instalowanie pakietu

Kliknij prawym przyciskiem myszy projekt i przejdź do Manage Nuget Packages witryny , aby zainstalować Azure.Communication.Calling.WindowsClientwersję 1.0.0 lub wyższą. Upewnij się, że zaznaczono opcję Uwzględnij wersję Preleased.

Żądanie dostępu

Zrzut ekranu przedstawiający żądanie dostępu do Internetu i mikrofonu w programie Visual Studio.

Dodaj następujący kod do pliku app.manifest:

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

Konfigurowanie struktury aplikacji

Musimy skonfigurować podstawowy układ, aby dołączyć naszą logikę. Aby umieścić wywołanie wychodzące, musimy TextBox podać identyfikator użytkownika wywoływanego. Potrzebujemy Start Call również przycisku i Hang Up przycisku. Musimy również wyświetlić podgląd lokalnego wideo i renderować zdalne wideo innego uczestnika. Potrzebujemy więc dwóch elementów do wyświetlania strumieni wideo.

MainWindow.xaml Otwórz projekt i zastąp zawartość następującą implementacją.

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

Otwórz do App.xaml.cs (kliknij prawym przyciskiem myszy i wybierz polecenie Wyświetl kod) i dodaj ten wiersz do góry:

using CallingQuickstart;

Otwórz (kliknij prawym przyciskiem MainWindow.xaml.cs myszy i wybierz pozycję Wyświetl kod) i zastąp zawartość następującą implementacją:

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

Model obiektów

Następujące klasy i interfejsy obsługują niektóre główne funkcje zestawu AZURE Communication Services Calling SDK:

Nazwa/nazwisko opis
CallClient Jest CallClient to główny punkt wejścia do biblioteki wywołującej klienta.
CallAgent Element służy do uruchamiania CallAgent i dołączania wywołań.
CommunicationCall Służy CommunicationCall do zarządzania umieszczonymi lub sprzężonym wywołaniami.
CallTokenCredential Element CallTokenCredential jest używany jako poświadczenie tokenu w celu utworzenia wystąpienia elementu CallAgent.
CommunicationUserIdentifier Element CommunicationUserIdentifier służy do reprezentowania tożsamości użytkownika, która może być jedną z następujących opcji: CommunicationUserIdentifier, PhoneNumberIdentifier lub CallingApplication.

Uwierzytelnianie użytkownika

Aby zainicjować element CallAgent, potrzebujesz tokenu dostępu użytkownika. Zazwyczaj ten token jest generowany na podstawie usługi z uwierzytelnianiem specyficznym dla aplikacji. Aby uzyskać więcej informacji na temat tokenów dostępu użytkowników, zapoznaj się z przewodnikiem Tokeny dostępu użytkowników.

W tym przewodniku Szybki start zastąp <AUTHENTICATION_TOKEN> ciąg tokenem dostępu użytkownika wygenerowanym dla zasobu usługi Azure Communication Service.

Po zainicjowaniu CallAgent wystąpienia tokenu, co umożliwi nam wykonywanie i odbieranie wywołań. Aby uzyskać dostęp do kamer na urządzeniu, musimy również uzyskać Menedżer urządzeń wystąpienie.

Dodaj następujący kod do InitCallAgentAndDeviceManagerAsync funkcji .

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;

Rozpoczynanie połączenia za pomocą wideo

Dodaj implementację do CallButton_Click elementu , aby rozpocząć wywołanie za pomocą wideo. Musimy wyliczyć aparaty za pomocą wystąpienia menedżera urządzeń i skonstruować LocalVideoStreamelement . Musimy ustawić VideoOptions element i LocalVideoStream przekazać go za startCallOptions pomocą , aby ustawić początkowe opcje dla wywołania. Dołączając LocalVideoStream do pliku MediaPlayerElement, możemy zobaczyć podgląd lokalnego wideo.

var startCallOptions = new StartCallOptions();

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

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

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

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

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

Akceptowanie połączenia przychodzącego

Dodaj implementację do Agent_OnIncomingCallAsync elementu , aby odpowiedzieć na połączenie przychodzące za pomocą wideo, przekaż element do acceptCallOptions.LocalVideoStream

var acceptCallOptions = new AcceptCallOptions();

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

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

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

call = await incomingCall.AcceptAsync(acceptCallOptions);

Zdalny uczestnik i zdalne strumienie wideo

Wszyscy uczestnicy zdalni są dostępni za pośrednictwem kolekcji w wystąpieniu RemoteParticipants wywołania. Po nawiązaniu połączenia możemy uzyskać dostęp do zdalnych uczestników połączenia i obsłużyć zdalne strumienie wideo.

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

Renderowanie zdalnych filmów wideo

Dla każdego zdalnego strumienia wideo dołącz go do .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();
        });
    }
}

Aktualizacja stanu wywołania

Musimy wyczyścić programy renderujących wideo po rozłączeniu wywołania i obsłużyć przypadek, gdy uczestnicy zdalni początkowo dołączą do wywołania.

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

Kończ połączenie

Zakończ bieżące wywołanie po kliknięciu Hang Up przycisku. Dodaj implementację do HangupButton_Click, aby zakończyć wywołanie utworzonego przez nas agenta callAgent i usunąć aktualizację uczestnika i wywołać programy obsługi zdarzeń stanu.

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

Uruchamianie kodu

Możesz skompilować i uruchomić kod w programie Visual Studio. W przypadku platform rozwiązań obsługujemy ARM64platformy , x64 i x86.

Możesz wykonać wychodzące połączenie wideo, podając identyfikator użytkownika w polu tekstowym i klikając Start Call przycisk.

Uwaga: wywołanie 8:echo123 zatrzymuje strumień wideo, ponieważ bot echo nie obsługuje przesyłania strumieniowego wideo.

Aby uzyskać więcej informacji na temat identyfikatorów użytkowników (tożsamości), zapoznaj się z przewodnikiem Tokeny dostępu użytkowników.