Начало работы с примером вызывающего героя
Главный пример функции группового вызова Служб коммуникации Azure демонстрирует, как создать возможность группового вызова с помощью веб-пакета SDK для вызовов Служб коммуникации.
В этом кратком руководстве мы узнаем, как работает пример перед запуском примера на локальном компьютере, а затем разверните пример в Azure с помощью собственных Службы коммуникации Azure ресурсов.
Скачать код
Найдите проект для этого примера на сайте GitHub. Версия примера, включающая функции в настоящее время в общедоступной предварительной версии, такие как взаимодействие Teams и запись звонков, можно найти в отдельной ветви.
Обзор
Пример содержит клиентское и серверное приложения. Клиентское приложение — это веб-приложение React/Redux, использующее платформу пользовательского интерфейса Fluent от Майкрософт. Это приложение отправляет запросы в серверное приложение ASP.NET Core, которое помогает клиентскому приложению подключаться к Azure.
Вот как выглядит этот пример:
При нажатии кнопки "Start a call" (Начать вызов) веб-приложение получает маркер доступа пользователя из серверного приложения. Затем этот маркер используется для подключения клиентского приложения к Службам коммуникации Azure. После получения маркера вам будет предложено указать камеру и микрофон, которые вы хотите использовать. Вы можете отключить или включить устройства с элементами управления переключателями:
Настройте отображаемое имя и устройства, а затем присоединитесь к сеансу вызова. Вы увидите основной холст вызова, в котором живет основной интерфейс вызова.
Ниже перечислены компоненты главного экрана вызова.
- Коллекция мультимедиа: основной этап, на котором показаны участники. Если у участника есть камера и она включена, здесь отображается видеопоток. Для каждого участника создается отдельная плитка, на которой отображаются его отображаемое имя и видеопоток (если он есть).
- Заголовок. Это место, где элементы управления основными вызовами находятся для переключения параметров и боковой панели участника, включения видео и отключения, совместного использования экрана и выхода из звонка.
- Боковая панель: здесь отображаются сведения о участниках и параметрах при переключениях с помощью элементов управления в заголовке. Этот компонент можно закрыть, щелкнув "X" в правом верхнем углу. На боковой панели участников отображается список участников и ссылка, чтобы пригласить других пользователей в чат. Боковая панель с параметрами позволяет настроить параметры микрофона и камеры.
Ниже приведены дополнительные сведения о предварительных требованиях и шагах по настройке примера.
Необходимые компоненты
- Учетная запись Azure с активной подпиской. Дополнительные сведения см. в разделе Создание учетной записи бесплатно.
- Node.js (12.18.4 и более поздние версии).
- Visual Studio Code (стабильная сборка)
- Ресурс Служб коммуникации Azure. Дополнительные сведения см. в кратком руководстве по созданию ресурсов Служб коммуникации и управлению ими. Для этого краткого руководства необходимо записать строка подключения ресурса.
Действия перед первым запуском примера
Откройте экземпляр PowerShell, Терминал Windows, командную строку или эквивалент и перейдите в каталог, в который вы хотите клонировать пример.
git clone https://github.com/Azure-Samples/communication-services-web-calling-hero.git
Connection String
Получите из портал Azure или с помощью Azure CLI.az communication list-key --name "<acsResourceName>" --resource-group "<resourceGroup>"
Дополнительные сведения о строках подключения см. в статье Создание ресурсов Служб коммуникации Azure и управление ими.
После получения
Connection String
файла добавьте строка подключения в файл samples/Server/appsetting.json. Сохраните строку подключения в переменную:ResourceConnectionString
.Endpoint string
Получите из портал Azure или с помощью Azure CLI.az communication list-key --name "<acsResourceName>" --resource-group "<resourceGroup>"
Дополнительные сведения о строках конечных точек см. в статье "Создание ресурсов коммуникации Azure"
После получения
Endpoint String
строки конечной точки добавьте строку конечной точки в файл samples/Server/appsetting.json . Ввод строки конечной точки в переменнойEndpointUrl
Локальный запуск
Установка зависимостей
npm run setup
Запуск вызывающего приложения
npm run start
Откроется клиентский сервер через порт 3000, обслуживающий файлы веб-сайта, и сервер API через порт 8080, который выполняет такие функции, как токены монетирования для участников вызова.
Устранение неполадок
В приложении отображается экран "Неподдерживаемый браузер", но я в поддерживаемом браузере.
Если ваше приложение обслуживается по имени узла, отличному от localhost, необходимо обслуживать трафик по https и не http.
Публикация в Azure
npm run setup
npm run build
npm run package
- Использование расширения Azure и развертывание каталога "Вызов/dist" в службе приложений
Очистка ресурсов
Если вы хотите отменить и удалить подписку на Службы коммуникации, можно удалить ресурс или группу ресурсов. При удалении группы ресурсов также удаляются все связанные с ней ресурсы. См. сведения об очистке ресурсов.
Следующие шаги
Дополнительные сведения см. в следующих статьях:
- Узнайте, как правильно использовать пакет SDK для вызовов.
- Узнайте больше о принципе работы функции вызовов.
Дополнительные материалы
- Примеры: дополнительные примеры см. на нашей странице обзора примеров.
- Redux — управление состоянием на стороне клиента
- FluentUI — библиотека пользовательского интерфейса, поддерживаемая корпорацией Майкрософт.
- React — библиотека для создания пользовательских интерфейсов.
- ASP.NET Core — платформа для создания веб-приложений.
Пример группового вызова для iOS Служб коммуникации Azure показывает, как с помощью пакета SDK для вызовов Служб коммуникации для iOS можно создавать групповые вызовы с поддержкой голоса и видео. В этом кратком руководстве описывается, как настроить и запустить этот пример. Для понимания контекста приводятся общие сведения о примере.
Скачать код
Найдите проект для этого примера на сайте GitHub.
Обзор
Этот пример содержит собственное приложение iOS, которое использует пакеты SDK Служб коммуникации Azure для iOS для создания вызовов с поддержкой голоса и видео. Это приложение использует компонент на стороне сервера для подготовки маркеров доступа, которые затем используются для инициализации пакета SDK Служб коммуникации Azure. Чтобы настроить этот компонент на стороне сервера, вы можете воспользоваться учебником по созданию доверенной службы с Функциями Azure.
Вот как выглядит этот пример:
При нажатии кнопки "Пуск нового вызова" приложение iOS предложит ввести отображаемое имя, которое будет использоваться для вызова.
После нажатия кнопки "Далее" на экране "Пуск вызова" у вас есть возможность поделиться идентификатором группы звонка с помощью листа общего ресурса iOS.
Приложение также позволяет присоединить существующий вызов Службы коммуникации Azure, указав идентификатор существующего вызова или ссылку идентификатора команды.
После присоединения к вызову вам будет предложено предоставить приложению разрешение на доступ к камере и микрофону, если он еще не авторизован. Помните, что, как и все приложения на основе AVFoundation, истинные функции аудио и видео доступны только на реальном оборудовании.
После настройки отображаемого имени и присоединения к вызову отобразится основной холст вызова, в котором живет основной интерфейс вызова.
Ниже перечислены компоненты главного экрана вызова.
- Коллекция мультимедиа: основной этап, на котором показаны участники. Если у участника есть камера и она включена, здесь отображается видеопоток. Для каждого участника создается отдельная плитка, на которой размещаются его отображаемое имя и видеопоток (если он есть). Коллекция поддерживает несколько участников и автоматически обновляется при добавлении или удалении участников вызова.
- Панель действий. Это место, где находятся основные элементы управления вызовами. Эти элементы управления позволяют включать и отключать видео и микрофон, предоставлять общий доступ к экрану и покидать сеанс вызова.
Ниже вы найдете дополнительные сведения о предварительных требованиях и шагах по настройке примера.
Необходимые компоненты
- Учетная запись Azure с активной подпиской. Дополнительные сведения см. на странице Создайте бесплатную учетную запись Azure уже сегодня.
- компьютер Mac с Xcode, а также действительный сертификат разработчика, установленный в цепочку ключей;
- Ресурс Служб коммуникации Azure. Дополнительные сведения см. в кратком руководстве по созданию ресурсов Служб коммуникации и управлению ими.
- Функция Azure, запускающая конечную точку проверки подлинности для получения маркеров доступа.
Локальное выполнение примера
Этот пример группового вызова можно запустить локально с помощью XCode. Разработчики могут использовать для тестирования приложения реальное устройство или эмулятор.
Действия перед первым запуском примера
- Установите зависимости, выполнив команду
pod install
. - Откройте
AzureCalling.xcworkspace
в XCode. - Создайте текстовый файл в корневом каталоге
AppSettings.xcconfig
и задайте значение:communicationTokenFetchUrl = <your authentication endpoint, without the https:// component>
Запуск примера
Создайте и запустите пример в XCode, используя целевой объект AzureCalling на выбранном симуляторе или устройстве.
(Необязательно) Защита конечной точки аутентификации
В демонстрационных целях в этом примере по умолчанию используется общедоступная конечная точка для получения маркера Служб коммуникации Azure. Для реальных рабочих сценариев мы рекомендуем создать собственную защищенную конечную точку и выдавать собственные маркеры.
При дополнительной конфигурации этот пример поддерживает подключение к защищенной конечной точке идентификатора Microsoft Entra ID (Microsoft Entra ID), чтобы имя входа пользователя требовалось для получения маркера доступа Службы коммуникации Azure. Соответствующие действия описаны ниже.
- Включите проверку подлинности Microsoft Entra в приложении.
- Перейдите на страницу обзора зарегистрированного приложения в разделе "Регистрация приложений Microsoft Entra". Запишите значения
Application (client) ID
,Directory (tenant) ID
,Application ID URI
.
- Создайте файл в корневом
AppSettings.xcconfig
каталоге, если он еще не присутствует, и добавьте значения:communicationTokenFetchUrl = <Application ID URI, without the https:// component> aadClientId = <Application (client) ID> aadTenantId = <Directory (tenant) ID>
Очистка ресурсов
Если вы хотите отменить и удалить подписку на Службы коммуникации, можно удалить ресурс или группу ресурсов. При удалении группы ресурсов также удаляются все связанные с ней ресурсы. См. сведения об очистке ресурсов.
Следующие шаги
Дополнительные сведения см. в следующих статьях:
- Узнайте, как правильно использовать пакет SDK для вызовов.
- Узнайте больше о принципе работы функции вызовов.
Дополнительные материалы
- Службы коммуникации Azure (GitHub) — дополнительные примеры и сведения на официальной странице GitHub
- Примеры: дополнительные примеры см. на нашей странице обзора примеров.
- Функции вызов Служб коммуникации Azure. Дополнительные сведения о пакете SDK для iOS см. в статье о пакетах SDK для iOS для вызовов в Службах коммуникации Azure
Пример группового вызова для Android Служб коммуникации Azure показывает, как с помощью пакета SDK для вызовов Служб коммуникации для Android можно создавать групповые вызовы с поддержкой голоса и видео. В этом кратком руководстве описывается, как настроить и запустить этот пример. Для понимания контекста приводятся общие сведения о примере.
Скачать код
Найдите проект для этого примера на сайте GitHub.
Обзор
Примером является собственное приложение Android, использующее клиентская библиотека пользовательского интерфейса Android Службы коммуникации Azure для создания интерфейса вызова, который включает как голосовой, так и видеозвонок. Это приложение использует компонент на стороне сервера для подготовки маркеров доступа, которые затем используются для инициализации пакета SDK Служб коммуникации Azure. Чтобы настроить этот компонент на стороне сервера, вы можете воспользоваться учебником по созданию доверенной службы с Функциями Azure.
Вот как выглядит этот пример:
При нажатии кнопки "Пуск нового вызова" приложение Android предложит ввести отображаемое имя, которое будет использоваться для вызова.
После нажатия кнопки "Далее" на странице "Пуск вызова" у вас есть возможность поделиться идентификатором группового вызова.
Приложение позволяет присоединить существующий вызов Службы коммуникации Azure, указав идентификатор существующего вызова или ссылку на собрание команды и отображаемое имя.
После присоединения к вызову вам будет предложено предоставить приложению разрешение на доступ к камере и микрофону, если он еще не авторизован. Вы увидите основной холст вызова с основными возможностями.
Ниже перечислены компоненты главного экрана вызова.
- Коллекция мультимедиа: основной этап, на котором показаны участники. Если у участника есть камера и она включена, здесь отображается видеопоток. Для каждого участника создается отдельная плитка, на которой размещаются его отображаемое имя и видеопоток (если он есть). Коллекция поддерживает несколько участников и автоматически обновляется при добавлении или удалении участников вызова.
- Панель действий. Это место, где находятся основные элементы управления вызовами. Эти элементы управления позволяют включать и отключать видео и микрофон, предоставлять общий доступ к экрану и покидать сеанс вызова.
Ниже вы найдете дополнительные сведения о предварительных требованиях и шагах по настройке примера.
Необходимые компоненты
- Учетная запись Azure с активной подпиской. Дополнительные сведения см. на странице Создайте бесплатную учетную запись Azure уже сегодня.
- Android Studio, установленное на вашем компьютере
- Ресурс Служб коммуникации Azure. Дополнительные сведения см. в кратком руководстве по созданию ресурсов Служб коммуникации и управлению ими.
- Функция Azure, запускающая конечную точку проверки подлинности для получения маркеров доступа.
Локальное выполнение примера
Этот пример группового вызова можно запустить локально с помощью Android Studio. Разработчики могут использовать для тестирования приложения реальное устройство или эмулятор.
Действия перед первым запуском примера
- Запустите Android Studio и выберите
Open an Existing Project
- Откройте папку
AzureCalling
в загруженном выпуске для примера. - Разверните ресурс или приложение, чтобы обновить
appSettings.properties
. Задайте для ключаcommunicationTokenFetchUrl
значение, соответствующее URL-адресу конечной точки проверки подлинности.
Запуск примера
Откройте пример в Android Studio.
(Необязательно) Защита конечной точки аутентификации
В демонстрационных целях в этом примере по умолчанию используется общедоступная конечная точка для получения маркера Служб коммуникации Azure. Для реальных рабочих сценариев мы рекомендуем создать собственную защищенную конечную точку и выдавать собственные маркеры.
При дополнительной конфигурации этот пример поддерживает подключение к защищенной конечной точке идентификатора Microsoft Entra ID (Microsoft Entra ID), чтобы имя входа пользователя требовалось для получения маркера Службы коммуникации Azure. Соответствующие действия описаны ниже.
Включите проверку подлинности Microsoft Entra в приложении.
Перейдите на страницу обзора зарегистрированного приложения в разделе "Регистрация приложений Microsoft Entra". Запишите
Package name
,Signature hash
.MSAL Configutaion
Измените
AzureCalling/app/src/main/res/raw/auth_config_single_account.json
и задайте дляisAADAuthEnabled
включения идентификатора Microsoft Entra.Измените
AndroidManifest.xml
и задайтеandroid:path
для хэша подписи хранилища ключей. (Необязательно. Текущее значение использует хэш из пакета debug.keystore. Если используется другое хранилище ключей, это необходимо обновить.)<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>
Скопируйте конфигурацию Android MSAL из портала Azure и вставьте в
AzureCalling/app/src/main/res/raw/auth_config_single_account.json
. Включить "account_mode": "SINGLE"{ "client_id": "", "authorization_user_agent": "DEFAULT", "redirect_uri": "", "account_mode" : "SINGLE", "authorities": [ { "type": "AAD", "audience": { "type": "AzureADMyOrg", "tenant_id": "" } } ] }
Измените
AzureCalling/app/src/main/res/raw/auth_config_single_account.json
и задайте для ключаcommunicationTokenFetchUrl
значение, соответствующее URL-адресу конечной точки проверки подлинности.Измените
AzureCalling/app/src/main/res/raw/auth_config_single_account.json
и задайте значение для ключаaadScopes
из областейExpose an API
Azure Active Directory
Задайте значение в
graphURL
AzureCalling/app/assets/appSettings.properties
качестве конечной точки API Graph, чтобы получить сведения о пользователе.Измените
AzureCalling/app/src/main/assets/appSettings.properties
и задайте значение ключаtenant
, чтобы включить автоматическое вход, чтобы пользователь не должен пройти проверку подлинности снова и снова при перезапуске приложения.
Очистка ресурсов
Если вы хотите отменить и удалить подписку на Службы коммуникации, можно удалить ресурс или группу ресурсов. При удалении группы ресурсов также удаляются все связанные с ней ресурсы. См. сведения об очистке ресурсов.
Следующие шаги
Дополнительные сведения см. в следующих статьях:
- Узнайте, как правильно использовать пакет SDK для вызовов.
- Узнайте больше о принципе работы функции вызовов.
Дополнительные материалы
- Службы коммуникации Azure (GitHub) — дополнительные примеры и сведения на официальной странице GitHub
- Примеры: дополнительные примеры см. на нашей странице обзора примеров.
- Функции вызовов Служб коммуникации Azure. Дополнительные сведения о пакете SDK для Android см. в статье о пакетах SDK для Android для вызовов в Службе коммуникации Azure
В примере Службы коммуникации Azure группового вызова для Windows показано, как пакет SDK для служб коммуникации, вызывающий Windows, можно использовать для создания интерфейса группового вызова, включающего голосовую связь и видео. В этом примере вы узнаете, как настроить и запустить пример. Для понимания контекста приводятся общие сведения о примере.
Из этого краткого руководства вы узнаете, как запустить видеозвонок 1:1 с помощью пакета SDK для вызовов Службы коммуникации Azure для Windows.
Пример кода UWP
Необходимые компоненты
Для работы с данным руководством вам потребуется:
Учетная запись Azure с активной подпиской. Создайте учетную запись бесплатно .
Установите Visual Studio 2022 с рабочей нагрузкой разработки универсальная платформа Windows.
Развернутый ресурс Служб коммуникации. Создайте ресурс Служб коммуникации. Для этого краткого руководства необходимо записать строка подключения.
Маркер доступа пользователя для Службы коммуникации Azure. Вы также можете использовать Azure CLI и выполнить команду с строка подключения для создания пользователя и маркера доступа.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Дополнительные сведения см. в статье "Создание маркеров доступа и управление ими" с помощью Azure CLI.
Установка
Создание проекта
Создайте в Visual Studio новый проект с помощью шаблона Пустое приложение (универсальное приложение Windows), чтобы настроить одностраничное приложение универсальной платформы Windows (UWP).
Установка пакета
Щелкните проект правой кнопкой мыши и перейдите к Manage Nuget Packages
установке Azure.Communication.Calling.WindowsClient
версии 1.2.0-beta.1 или более поздней версии. Убедитесь, что установлен флажок включить предварительную проверку.
Запрос на доступ
Перейдите к Package.appxmanifest
и щелкните Capabilities
.
Установите флажок Internet (Client & Server)
, чтобы получить входящий и исходящий доступ в Интернет.
Установите флажок Microphone
, чтобы получить доступ к звуковому каналу микрофона.
Установите флажок WebCam
для доступа к камере устройства.
Добавьте следующий код в Package.appxmanifest
, щелкнув правой кнопкой мыши и выбрав пункт "Просмотреть код".
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>RtmMvrUap.dll</Path>
<ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
Настройка платформы приложения
Необходимо настроить базовую структуру для подключения нашей логики. Чтобы разместить исходящий вызов, необходимо TextBox
указать идентификатор пользователя вызываемого абонента. Будут также необходимы кнопки Start Call
и Hang Up
.
Кроме того, потребуется предварительный просмотр локального видео и отрисовка удаленного видео собеседника. Поэтому нам нужно два элемента для отображения видеопотоков.
Откройте файл MainPage.xaml
своего проекта и замените его содержимое следующей реализацией.
<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>
Откройте файл App.xaml.cs
(щелкните правой кнопкой мыши и выберите "Просмотреть код") и добавьте следующую строку в начало.
using CallingQuickstart;
Откройте файл MainPage.xaml.cs
(щелкните правой кнопкой мыши и выберите "Просмотреть код") и замените его содержимое следующей реализацией.
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
}
}
}
Объектная модель
Следующие классы и интерфейсы реализуют некоторые основные функции пакета SDK Служб коммуникации Azure для вызовов.
Имя | Описание |
---|---|
CallClient |
Это CallClient основная точка входа в клиентную библиотеку вызовов. |
CallAgent |
Используется CallAgent для запуска и присоединения вызовов. |
CommunicationCall |
Используется CommunicationCall для управления размещенными или присоединенными вызовами. |
CallTokenCredential |
Используется CallTokenCredential в качестве учетных данных маркера для создания экземпляра CallAgent . |
CommunicationUserIdentifier |
Используется CommunicationUserIdentifier для представления удостоверения пользователя, который может быть одним из следующих параметров: CommunicationUserIdentifier PhoneNumberIdentifier или CallingApplication . |
аутентификация клиента;
Для инициализации CallAgent
необходимо маркер доступа пользователя. Как правило, этот маркер создается из службы с проверкой подлинности, конкретной для приложения. Дополнительные сведения о маркерах доступа пользователей см. в руководстве по маркерам доступа пользователей.
Для целей этого краткого руководства замените <AUTHENTICATION_TOKEN>
маркером доступа пользователя, который был создан для вашего ресурса Службы коммуникации Azure.
После получения маркера инициализируйте CallAgent
экземпляр с ним, что позволяет нам совершать и принимать вызовы. Для доступа к камерам на устройстве также необходимо получить диспетчер устройств экземпляр.
Добавьте в функцию InitCallAgentAndDeviceManagerAsync
следующий код.
this.callClient = new CallClient(new CallClientOptions() {
Diagnostics = new CallDiagnosticsOptions() {
AppName = "CallingQuickstart",
AppVersion="1.0",
Tags = new[] { "Calling", "ACS", "Windows" }
}
});
// Set up local video stream using the first camera enumerated
var deviceManager = await this.callClient.GetDeviceManagerAsync();
var camera = deviceManager?.Cameras?.FirstOrDefault();
var mic = deviceManager?.Microphones?.FirstOrDefault();
micStream = new LocalOutgoingAudioStream();
CameraList.ItemsSource = deviceManager.Cameras.ToList();
if (camera != null)
{
CameraList.SelectedIndex = 0;
}
callTokenRefreshOptions = new CallTokenRefreshOptions(false);
callTokenRefreshOptions.TokenRefreshRequested += OnTokenRefreshRequestedAsync;
var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);
var callAgentOptions = new CallAgentOptions()
{
DisplayName = "Contoso",
//https://github.com/lukes/ISO-3166-Countries-with-Regional-Codes/blob/master/all/all.csv
EmergencyCallOptions = new EmergencyCallOptions() { CountryCode = "840" }
};
try
{
this.callAgent = await this.callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
//await this.callAgent.RegisterForPushNotificationAsync(await this.RegisterWNS());
this.callAgent.CallsUpdated += OnCallsUpdatedAsync;
this.callAgent.IncomingCallReceived += OnIncomingCallAsync;
}
catch(Exception ex)
{
if (ex.HResult == -2147024809)
{
// E_INVALIDARG
// Handle possible invalid token
}
}
Совершение видеозвонка
Добавьте реализацию в CallButton_Click
начало вызова с видео. необходимо перечислить камеры с помощью экземпляра Диспетчера устройств и сформировать LocalOutgoingVideoStream
. Нужно указать параметры VideoOptions
с аргументом LocalVideoStream
и передать их в startCallOptions
, чтобы задать начальные параметры звонка. Подключив LocalOutgoingVideoStream
к ней MediaElement
, можно просмотреть предварительную версию локального видео.
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;
}
Добавьте методы для запуска или присоединения к различным типам звонков (звонок 1:1 Службы коммуникации Azure, телефонный звонок 1:1, Службы коммуникации Azure групповой звонок, присоединение к собранию Teams и т. д.).
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 } }
};
}
Добавьте код для создания LocalVideoStream в зависимости от выбранной камеры метода 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);
}
Прием входящего вызова
Добавьте реализацию в OnIncomingCallAsync
ответ на входящий звонок с видео, передайте егоacceptCallOptions
LocalVideoStream
.
var incomingCall = args.IncomingCall;
var acceptCallOptions = new AcceptCallOptions() {
IncomingVideoOptions = new IncomingVideoOptions()
{
IncomingVideoStreamKind = VideoStreamKind.RemoteIncoming
}
};
_ = await incomingCall.AcceptAsync(acceptCallOptions);
Удаленный участник и видеопотоки
Все удаленные участники доступны через коллекцию RemoteParticipants
в экземпляре вызова. После подключенияCallState.Connected
вызова можно получить доступ к удаленным участникам вызова и обработать удаленные видеопотоки.
Примечание.
Когда пользователь присоединяется к вызову, он может получить доступ к текущим удаленным участникам через коллекцию RemoteParticipants
. Событие RemoteParticipantsUpdated
не будет запускаться для этих существующих участников. Это событие активируется только в том случае, если удаленный участник присоединяется или покидает звонок, пока пользователь уже находится в вызове.
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;
}
}
Отрисовка удаленного видео
Каждый удаленный видеопоток необходимо подключить к 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();
});
}
}
Обновление состояния вызова
После отключения вызова необходимо очистить отрисовщики видео и обработать ситуацию, когда удаленные участники первоначально присоединяются к вызову.
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;
}
}
Завершение вызова
Текущий вызов завершается при нажатии кнопки Hang Up
. Добавьте реализацию в HangupButton_Click, чтобы завершить вызов с помощью созданного вызова CallAgent, и сломать обработчики событий состояния участника.
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
try
{
await call.HangUpAsync(new HangUpOptions() { ForEveryone = true });
}
catch(Exception ex)
{
}
}
Выполнение кода
Вы можете выполнить сборку и запустить код в Visual Studio. Для платформ решений мы поддерживаем ARM64
x64
и x86
.
Чтобы совершить исходящий видеозвонок, укажите в текстовом поле ИД пользователя и щелкните кнопку Start Call
.
Примечание. Вызов 8:echo123
останавливает видеопоток, так как эхо-бот не поддерживает потоковую передачу видео.
Дополнительные сведения об идентификаторах пользователей см. в этом руководстве.
Пример кода WinUI 3
Необходимые компоненты
Для работы с данным руководством вам потребуется:
Учетная запись Azure с активной подпиской. Создайте учетную запись бесплатно .
Установите Visual Studio 2022 и пакет SDK для приложений Windows версии 1.2( предварительная версия 2).
Базовое понимание того, как создать приложение WinUI 3. Создайте первый проект WinUI 3 (пакет SDK для приложений Windows) — хороший ресурс для начала.
Развернутый ресурс Служб коммуникации. Создайте ресурс Служб коммуникации. Для этого краткого руководства необходимо записать строка подключения.
Маркер доступа пользователя для Службы коммуникации Azure. Вы также можете использовать Azure CLI и выполнить команду с строка подключения для создания пользователя и маркера доступа.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Дополнительные сведения см. в статье "Создание маркеров доступа и управление ими" с помощью Azure CLI.
Установка
Создание проекта
В Visual Studio создайте проект с шаблоном "Пустое приложение" (WinUI 3 в классическом приложении) для настройки одностраничного приложения WinUI 3.
Установка пакета
Щелкните проект правой кнопкой мыши и перейдите к Manage Nuget Packages
установке Azure.Communication.Calling.WindowsClient
версии 1.0.0 или более поздней версии. Убедитесь, что установлен флажок включить предварительную проверку.
Запрос на доступ
Добавьте в ваш app.manifest
код следующий код:
<file name="RtmMvrMf.dll">
<activatableClass name="VideoN.VideoSchemeHandler" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1" />
</file>
Настройка платформы приложения
Необходимо настроить базовую структуру для подключения нашей логики. Чтобы разместить исходящий вызов, необходимо TextBox
указать идентификатор пользователя вызываемого абонента. Будут также необходимы кнопки Start Call
и Hang Up
.
Кроме того, потребуется предварительный просмотр локального видео и отрисовка удаленного видео собеседника. Поэтому нам нужно два элемента для отображения видеопотоков.
Откройте файл MainWindow.xaml
своего проекта и замените его содержимое следующей реализацией.
<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>
Откройте файл App.xaml.cs
(щелкните правой кнопкой мыши и выберите "Просмотреть код") и добавьте следующую строку в начало.
using CallingQuickstart;
Откройте файл MainWindow.xaml.cs
(щелкните правой кнопкой мыши и выберите "Просмотреть код") и замените его содержимое следующей реализацией.
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();
});
}
}
}
Объектная модель
Следующие классы и интерфейсы реализуют некоторые основные функции пакета SDK Служб коммуникации Azure для вызовов.
Имя | Описание |
---|---|
CallClient |
Это CallClient основная точка входа в клиентную библиотеку вызовов. |
CallAgent |
Используется CallAgent для запуска и присоединения вызовов. |
CommunicationCall |
Используется CommunicationCall для управления размещенными или присоединенными вызовами. |
CallTokenCredential |
Используется CallTokenCredential в качестве учетных данных маркера для создания экземпляра CallAgent . |
CommunicationUserIdentifier |
Используется CommunicationUserIdentifier для представления удостоверения пользователя, который может быть одним из следующих параметров: CommunicationUserIdentifier PhoneNumberIdentifier или CallingApplication . |
аутентификация клиента;
Для инициализации CallAgent
необходимо маркер доступа пользователя. Как правило, этот маркер создается из службы с проверкой подлинности, конкретной для приложения. Дополнительные сведения о маркерах доступа пользователей см. в руководстве по маркерам доступа пользователей.
Для целей этого краткого руководства замените <AUTHENTICATION_TOKEN>
маркером доступа пользователя, который был создан для вашего ресурса Службы коммуникации Azure.
После инициализации экземпляра CallAgent
маркера, который позволяет нам совершать и принимать звонки. Для доступа к камерам на устройстве также необходимо получить диспетчер устройств экземпляр.
Добавьте в функцию InitCallAgentAndDeviceManagerAsync
следующий код.
var callClient = new CallClient();
this.deviceManager = await callClient.GetDeviceManagerAsync();
var tokenCredential = new CallTokenCredential("<AUTHENTICATION_TOKEN>");
var callAgentOptions = new CallAgentOptions()
{
DisplayName = "<DISPLAY_NAME>"
};
this.callAgent = await callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
this.callAgent.OnCallsUpdated += Agent_OnCallsUpdatedAsync;
this.callAgent.OnIncomingCall += Agent_OnIncomingCallAsync;
Совершение видеозвонка
Добавьте реализацию в CallButton_Click
начало вызова с видео. необходимо перечислить камеры с помощью экземпляра Диспетчера устройств и сформировать LocalVideoStream
. Нужно указать параметры VideoOptions
с аргументом LocalVideoStream
и передать их в startCallOptions
, чтобы задать начальные параметры звонка. Подключив LocalVideoStream
к ней MediaPlayerElement
, можно просмотреть предварительную версию локального видео.
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;
Прием входящего вызова
Добавьте реализацию в Agent_OnIncomingCallAsync
ответ на входящий звонок с видео, передайте его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);
Удаленный участник и видеопотоки
Все удаленные участники доступны через коллекцию RemoteParticipants
в экземпляре вызова. После подключения вызова можно получить доступ к удаленным участникам вызова и обработать удаленные видеопотоки.
Примечание.
Когда пользователь присоединяется к вызову, он может получить доступ к текущим удаленным участникам через коллекцию RemoteParticipants
. Событие OnRemoteParticipantsUpdated
не будет запускаться для этих существующих участников. Это событие активируется только в том случае, если удаленный участник присоединяется или покидает звонок, пока пользователь уже находится в вызове.
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);
}
}
Отрисовка удаленного видео
Каждый удаленный видеопоток необходимо подключить к 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();
});
}
}
Обновление состояния вызова
После отключения вызова необходимо очистить отрисовщики видео и обработать ситуацию, когда удаленные участники первоначально присоединяются к вызову.
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;
}
}
Завершение вызова
Текущий вызов завершается при нажатии кнопки Hang Up
. Добавьте реализацию в HangupButton_Click, чтобы завершить вызов с помощью созданного вызова CallAgent, и сломать обработчики событий состояния участника.
this.call.OnRemoteParticipantsUpdated -= Call_OnRemoteParticipantsUpdatedAsync;
this.call.OnStateChanged -= Call_OnStateChangedAsync;
await this.call.HangUpAsync(new HangUpOptions());
Выполнение кода
Вы можете выполнить сборку и запустить код в Visual Studio. Для платформ решений мы поддерживаем ARM64
x64
и x86
.
Чтобы совершить исходящий видеозвонок, укажите в текстовом поле ИД пользователя и щелкните кнопку Start Call
.
Примечание. Вызов 8:echo123
останавливает видеопоток, так как эхо-бот не поддерживает потоковую передачу видео.
Дополнительные сведения об идентификаторах пользователей см. в этом руководстве.