Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Узнайте, как управлять видеозвонками с помощью пакетов SDK служб коммуникации Azure. Мы узнаем, как управлять получением и отправкой видео в рамках звонка.
Предпосылки
- Учетная запись Azure с активной подпиской. Создайте учетную запись бесплатно .
- Ресурс служб связи, находящийся в эксплуатации. Создайте ресурс служб коммуникации.
- Токен доступа пользователя для авторизации клиента на совершение вызовов. Дополнительные сведения см. в статье "Создание маркеров доступа и управление ими".
- Необязательно: выполните быстрый запуск, чтобы добавить голосовые вызовы в ваше приложение
Установите SDK
Используйте команду npm install
, чтобы установить пакет SDK "Azure Communication Services Common and Calling" для JavaScript.
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Инициализируйте необходимые объекты
Для большинства операций вызова требуется экземпляр CallClient
. Когда вы создаёте новый экземпляр CallClient
, вы можете настроить его с помощью собственных параметров, подобных экземпляру Logger
.
С помощью экземпляра CallClient
вы можете создать экземпляр CallAgent
, вызвав createCallAgent
. Этот метод асинхронно возвращает объект экземпляра CallAgent
.
Метод createCallAgent
использует CommunicationTokenCredential
в качестве аргумента. Он принимает токен доступа пользователя.
Вы можете использовать метод getDeviceManager
на экземпляре CallClient
, чтобы получить доступ к deviceManager
.
const { CallClient } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential} = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the logger's log level
setLogLevel('verbose');
// Redirect log output to console, file, buffer, REST API, or whatever location you want
AzureLogger.log = (...args) => {
console.log(...args); // Redirect log output to console
};
const userToken = '<USER_TOKEN>';
callClient = new CallClient(options);
const tokenCredential = new AzureCommunicationTokenCredential(userToken);
const callAgent = await callClient.createCallAgent(tokenCredential, {displayName: 'optional Azure Communication Services user name'});
const deviceManager = await callClient.getDeviceManager()
Управление подключением пакета SDK к инфраструктуре Майкрософт
Экземпляр Call Agent
помогает вам управлять звонками (присоединяться к ним или начинать их). Для работы пакета SDK для вызова необходимо подключиться к инфраструктуре Майкрософт для получения уведомлений о входящих звонках и координации других сведений о вызове. Ваш Call Agent
имеет два возможных состояния:
Подключено - Значение Call Agent
connectionStatue, равное Connected
, означает, что клиентский SDK подключен и способен получать уведомления от инфраструктуры Microsoft.
Disconnected - Значение Call Agent
connectionStatue Disconnected
указывает, что есть проблема, которая препятствует правильному подключению SDK.
Call Agent
должно быть воссоздано.
-
invalidToken
: Если токен истек или недействителен,Call Agent
экземпляр отключается с этой ошибкой. -
connectionIssue
: если у клиента возникла проблема с подключением к инфраструктуре Майкрософт, после многочисленных повторных попытокCall Agent
возникаетconnectionIssue
ошибка.
Вы можете проверить, подключен ли ваш локальный Call Agent
к инфраструктуре Microsoft, проверив текущее значение свойства connectionState
. Во время активного звонка вы можете прослушать событие connectionStateChanged
, чтобы определить, изменилось ли Call Agent
из состояния Connected в состояние Disconnected.
const connectionState = callAgentInstance.connectionState;
console.log(connectionState); // it may return either of 'Connected' | 'Disconnected'
const connectionStateCallback = (args) => {
console.log(args); // it will return an object with oldState and newState, each of having a value of either of 'Connected' | 'Disconnected'
// it will also return reason, either of 'invalidToken' | 'connectionIssue'
}
callAgentInstance.on('connectionStateChanged', connectionStateCallback);
Управление устройствами
Чтобы начать использовать видео с SDK для вызовов, вам необходимо уметь управлять устройствами. Устройства позволяют вам контролировать передачу звука и видео в вызов.
Используйте deviceManager
, чтобы перечислить локальные устройства, которые могут передавать ваши аудио- и видеопотоки в вызове. Вы также можете использовать deviceManager
, чтобы запросить разрешение на доступ к микрофонам и камерам локального устройства.
Вы можете получить доступ к deviceManager
, вызвав метод callClient.getDeviceManager()
.
const deviceManager = await callClient.getDeviceManager();
Получение локальных устройств
Чтобы получить доступ к локальным устройствам, можно использовать методы перечисления deviceManager
, getCameras()
и getMicrophones
. Эти методы являются асинхронными действиями.
// Get a list of available video devices for use.
const localCameras = await deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
// Get a list of available microphone devices for use.
const localMicrophones = await deviceManager.getMicrophones(); // [AudioDeviceInfo, AudioDeviceInfo...]
// Get a list of available speaker devices for use.
const localSpeakers = await deviceManager.getSpeakers(); // [AudioDeviceInfo, AudioDeviceInfo...]
Установите устройства по умолчанию
Как только вы узнаете, какие устройства доступны для использования, вы можете установить устройства по умолчанию для микрофона, динамика и камеры. Если значения по умолчанию для клиента не заданы, пакет SDK служб коммуникации использует значения по умолчанию операционной системы.
Микрофон
Получите доступ к использованному устройству
// Get the microphone device that is being used.
const defaultMicrophone = deviceManager.selectedMicrophone;
Настройка устройства для использования
// Set the microphone device to use.
await deviceManager.selectMicrophone(localMicrophones[0]);
Спикер
Получите доступ к использованному устройству
// Get the speaker device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Настройка устройства для использования
// Set the speaker device to use.
await deviceManager.selectSpeaker(localSpeakers[0]);
Фотоаппарат
Получите доступ к использованному устройству
// Get the camera device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Настройка устройства для использования
// Set the speaker device to use.
await deviceManager.selectSpeaker(localCameras[0]);
Каждый CallAgent
может выбрать свои микрофон и колонки на связанном с ним DeviceManager
устройстве. Мы рекомендуем, чтобы разные CallAgents
использовали разные микрофоны и колонки. Им не следует использовать одни и те же микрофоны или динамики. Если происходит совместное использование, возможно, будет запущена диагностика, ориентированная на пользователя микрофона (UFD), и микрофон перестанет работать в зависимости от браузера и операционной системы.
Локальный видеопоток
Чтобы пользователи отправляли видео в вызове, необходимо создать LocalVideoStream
объект.
const localVideoStream = new LocalVideoStream(camera);
Камера, передаваемая в качестве параметра, является VideoDeviceInfo
объектом, возвращаемым методом deviceManager.getCameras()
.
A LocalVideoStream
имеет следующие свойства:
source
— это сведения об устройстве.const source = localVideoStream.source;
mediaStreamType
может бытьVideo
,ScreenSharing
илиRawMedia
.const type: MediaStreamType = localVideoStream.mediaStreamType;
Локальный предварительный просмотр камеры
Вы можете использовать deviceManager
и VideoStreamRenderer
, чтобы начать отображение потоков с вашей локальной камеры.
После создания LocalVideoStream
используйте его для настройки VideoStreamRenderer
. После создания VideoStreamRenderer
, вызовите его метод createView()
, чтобы получить представление, которое можно добавить в качестве дочернего элемента на вашу страницу.
Этот поток не отправляется другим участникам. Локальная лента предварительного просмотра.
// To start viewing local camera preview
const cameras = await deviceManager.getCameras();
const camera = cameras[0];
const localVideoStream = new LocalVideoStream(camera);
const videoStreamRenderer = new VideoStreamRenderer(localVideoStream);
const view = await videoStreamRenderer.createView();
htmlElement.appendChild(view.target);
Остановить локальный просмотр
Чтобы остановить локальный предварительный просмотр, воспользуйтесь методом dispose у представления, унаследованного от VideoStreamRenderer
.
После удаления VideoStreamRenderer, удалите представление из html-дерева, вызвав метод removeChild()
из узла DOM, содержащего ваш предварительный просмотр.
// To stop viewing local camera preview
view.dispose();
htmlElement.removeChild(view.target);
Запрос разрешения на камеру и микрофон
Приложение не может использовать камеру или микрофон без разрешений. С помощью deviceManager можно предложить пользователю предоставить разрешения камеры и (или) микрофона:
const result = await deviceManager.askDevicePermission({audio: true, video: true});
После того как обещание выполнено, метод возвращает объект DeviceAccess
, который указывает, были ли даны разрешения audio
и video
.
console.log(result.audio);
console.log(result.video);
Примечания.
-
videoDevicesUpdated
событие возникает при подключении и отключении видеоустройств. -
audioDevicesUpdated
событие срабатывает при подключении аудио устройств. - При первом создании
DeviceManager
, он не знает о каких-либо устройствах, если разрешения еще не предоставлены. Первоначально имя устройства пусто, и оно не содержит подробных сведений об устройстве. Необходимо вызватьDeviceManager.askPermission()
, что запрашивает у пользователя доступ к устройству. Когда пользователь разрешает доступ, диспетчер устройств узнает об устройствах в системе, обновляет списки устройств и отправляет событияaudioDevicesUpdated
иvideoDevicesUpdated
. Если пользователь обновляет страницу и создает диспетчер устройств, диспетчер устройств узнает об устройствах, так как пользователь ранее предоставил доступ. Он имеет свои списки устройств, заполненные изначально, и не генерирует событияaudioDevicesUpdated
илиvideoDevicesUpdated
. - Перечисление/выбор динамиков не поддерживается в Android Chrome, iOS Safari и macOS Safari.
Совершить видеовызов
Важно
В настоящее время поддерживается только один исходящий локальный видеопоток.
Чтобы совершить видеозвонок, вы должны перечислить местные камеры, используя метод getCameras()
в deviceManager
.
После выбора камеры используйте ее для создания экземпляра LocalVideoStream
.
Передайте его в videoOptions
виде элемента в localVideoStream
массиве методу CallAgent
startCall
.
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
const placeCallOptions = {videoOptions: {localVideoStreams:[localVideoStream]}};
const userCallee = { communicationUserId: '<ACS_USER_ID>' }
const call = callAgent.startCall([userCallee], placeCallOptions);
- Вы также можете присоединиться к видеозвонку с помощью API
CallAgent.join()
, а также принимать и совершать видеозвонки с помощью APICall.Accept()
. - Когда ваш звонок соединяется, он автоматически начинает отправку видеопотока с выбранной камеры другому участнику.
Начать и остановить отправку локального видео во время звонка
Запустить видео
Чтобы запустить видео во время вызова, необходимо перечислить камеры с помощью getCameras
метода объекта deviceManager
.
Затем создайте новый экземпляр LocalVideoStream
с нужной камерой, а затем передайте объект LocalVideoStream
в метод startVideo
существующего объекта вызова.
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);
Остановить видео
После успешного начала передачи видео экземпляр LocalVideoStream
типа Video
добавляется в коллекцию localVideoStreams
в экземпляре вызова.
Найдите видео поток в объекте вызова
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'Video'} );
Остановить локальное видео Чтобы остановить трансляцию локального видео во время звонка, передайте экземпляр localVideoStream
, который используется для видео, методу stopVideo объекта Call
.
await call.stopVideo(localVideoStream);
Вы можете переключиться на другое устройство камеры, имея активный LocalVideoStream, вызвав switchSource
на этом экземпляре LocalVideoStream
.
const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);
Если указанное видео устройство недоступно:
- Во время звонка, если ваше видео выключено и вы запускаете видео с помощью
call.startVideo()
, этот метод вызываетSourceUnavailableError
, и диагностический параметрcameraStartFailed
, обращенный к пользователю, устанавливается в значение "истина". - Вызов метода
localVideoStream.switchSource()
вызывает присвоение значения true переменнойcameraStartFailed
. В нашем руководстве по диагностике звонков содержатся дополнительные сведения о том, как диагностировать связанные с вызовом проблемы.
Чтобы проверить, включено или выключено локальное видео, вы можете использовать метод Call
isLocalVideoStarted
, который возвращает true или false.
// Check if local video is on or off
call.isLocalVideoStarted;
Чтобы отслеживать изменения локального видео, вы можете подписаться и отписаться от события isLocalVideoStartedChanged.
// Subscribe to local video event
call.on('isLocalVideoStartedChanged', () => {
// Callback();
});
// Unsubscribe from local video event
call.off('isLocalVideoStartedChanged', () => {
// Callback();
});
Начало и остановка демонстрации экрана во время звонка
Чтобы начать демонстрацию экрана во время звонка, вы можете использовать асинхронный метод startScreenSharing()
на объекте Call
.
Начать демонстрацию экрана
// Start screen sharing
await call.startScreenSharing();
Примечание
Отправка общего доступа к экрану поддерживается только для настольных браузеров.
Найдите совместное использование экрана в коллекции LocalVideoStream.
После успешной отправки общего доступа LocalVideoStream
к экрану экземпляр типа ScreenSharing
добавляется в localVideoStreams
коллекцию экземпляра вызова.
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing'} );
Остановить демонстрацию экрана
Чтобы остановить демонстрацию экрана во время звонка, вы можете использовать асинхронный API stopScreenSharing.
// Stop screen sharing
await call.stopScreenSharing();
Проверка состояния общего доступа к экрану
Чтобы проверить, включен ли экранный режим или выключен, вы можете использовать API isScreenSharingOn, который возвращает true или false.
// Check if screen sharing is on or off
call.isScreenSharingOn;
Чтобы отслеживать изменения в трансляции экрана, вы можете подписаться и отписаться от события isScreenSharingOnChanged.
// Subscribe to screen share event
call.on('isScreenSharingOnChanged', () => {
// Callback();
});
// Unsubscribe from screen share event
call.off('isScreenSharingOnChanged', () => {
// Callback();
});
Важно
Эта функция Azure Communication Services в настоящее время находится в стадии предварительного просмотра. Функции в предварительной версии общедоступны и могут использоваться всеми новыми и существующими клиентами Майкрософт.
Предварительные API и SDK предоставляются без соглашения об уровне обслуживания. Мы рекомендуем не использовать их для рабочих нагрузок на производстве. Некоторые функции могут не поддерживаться или могут быть ограничены.
Для получения дополнительной информации см. Дополнительные условия использования для предварительных версий Microsoft Azure.
Локальный предварительный просмотр демонстрации экрана находится в публичном предварительном доступе и доступен как часть версии 1.15.1-beta.1+.
Локальный предварительный просмотр экрана для совместного использования
Вы можете использовать VideoStreamRenderer
, чтобы начать визуализацию потоков с вашего локального экрана, чтобы видеть, что вы отправляете в виде потока совместного использования экрана.
// To start viewing local screen share preview
await call.startScreenSharing();
const localScreenSharingStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing' });
const videoStreamRenderer = new VideoStreamRenderer(localScreenSharingStream);
const view = await videoStreamRenderer.createView();
htmlElement.appendChild(view.target);
// To stop viewing local screen share preview.
await call.stopScreenSharing();
view.dispose();
htmlElement.removeChild(view.target);
// Screen sharing can also be stopped by clicking on the native browser's "Stop sharing" button.
// The isScreenSharingOnChanged event will be triggered where you can check the value of call.isScreenSharingOn.
// If the value is false, then that means screen sharing is turned off and so we can go ahead and dispose the screen share preview.
// This event is also triggered for the case when stopping screen sharing via Call.stopScreenSharing() API.
call.on('isScreenSharingOnChanged', () => {
if (!call.isScreenSharingOn) {
view.dispose();
htmlElement.removeChild(view.target);
}
});
Воспроизведение потоков видео и экранного вещания удаленных участников
Чтобы отобразить видео удаленного участника или совместное использование экрана, первым шагом нужно получить ссылку на RemoteVideoStream, который вы хотите отобразить.
Вы можете отрисовать удаленного участника, используя массив или видеопоток (videoStreams
) объекта RemoteParticipant
. Доступ к коллекции удаленных участников осуществляется через Call
объект.
const remoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType = remoteVideoStream.mediaStreamType;
Для отрисовки RemoteVideoStream
необходимо подписаться на событие isAvailableChanged
. Если свойство isAvailable
изменяется на true
, удаленный участник отправляет поток видео.
После этого создайте новый экземпляр VideoStreamRenderer
, а затем создайте новый экземпляр VideoStreamRendererView
, используя асинхронный метод createView
. Затем вы можете присоединить view.target
к любому элементу пользовательского интерфейса.
При каждом изменении доступности удаленного потока можно уничтожить все VideoStreamRenderer
или конкретное VideoStreamRendererView
. Если вы решите сохранить их, то в представлении отображается пустой кадр видео.
// Reference to the html's div where we would display a grid of all remote video stream from all participants.
let remoteVideosGallery = document.getElementById('remoteVideosGallery');
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
let renderer = new VideoStreamRenderer(remoteVideoStream);
let view;
let remoteVideoContainer = document.createElement('div');
remoteVideoContainer.className = 'remote-video-container';
let loadingSpinner = document.createElement('div');
// See the css example below for styling the loading spinner.
loadingSpinner.className = 'loading-spinner';
remoteVideoStream.on('isReceivingChanged', () => {
try {
if (remoteVideoStream.isAvailable) {
const isReceiving = remoteVideoStream.isReceiving;
const isLoadingSpinnerActive = remoteVideoContainer.contains(loadingSpinner);
if (!isReceiving && !isLoadingSpinnerActive) {
remoteVideoContainer.appendChild(loadingSpinner);
} else if (isReceiving && isLoadingSpinnerActive) {
remoteVideoContainer.removeChild(loadingSpinner);
}
}
} catch (e) {
console.error(e);
}
});
const createView = async () => {
// Create a renderer view for the remote video stream.
view = await renderer.createView();
// Attach the renderer view to the UI.
remoteVideoContainer.appendChild(view.target);
remoteVideosGallery.appendChild(remoteVideoContainer);
}
// Remote participant has switched video on/off
remoteVideoStream.on('isAvailableChanged', async () => {
try {
if (remoteVideoStream.isAvailable) {
await createView();
} else {
view.dispose();
remoteVideosGallery.removeChild(remoteVideoContainer);
}
} catch (e) {
console.error(e);
}
});
// Remote participant has video on initially.
if (remoteVideoStream.isAvailable) {
try {
await createView();
} catch (e) {
console.error(e);
}
}
console.log(`Initial stream size: height: ${remoteVideoStream.size.height}, width: ${remoteVideoStream.size.width}`);
remoteVideoStream.on('sizeChanged', () => {
console.log(`Remote video stream size changed: new height: ${remoteVideoStream.size.height}, new width: ${remoteVideoStream.size.width}`);
});
}
CSS для стилизации индикатора загрузки поверх удаленного видео потока.
.remote-video-container {
position: relative;
}
.loading-spinner {
border: 12px solid #f3f3f3;
border-radius: 50%;
border-top: 12px solid #ca5010;
width: 100px;
height: 100px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
Качество удалённого видео
Пакет SDK для веб-служб коммуникации Azure предоставляет функцию "Оптимальное число видео" (OVC), начиная с версии 1.15.1.
Используйте эту функцию, чтобы информировать приложения во время их работы о том, сколько входящих видео от разных участников может быть оптимально отображено в любой момент во время группового вызова из двух или более участников.
Эта функция выявляет свойство optimalVideoCount
, которое динамически изменяется во время вызова в зависимости от возможностей сети и оборудования локального конечного устройства. Значение optimalVideoCount
описывает, сколько видео из различных приложений участников должно быть отображено в данный момент. Приложения должны учитывать эти изменения и обновлять количество отображаемых видео в соответствии с рекомендациями. Между каждым обновлением существует задержка (около десяти (10) секунд).
Использование
Эта optimalVideoCount
функция является функцией вызова. Вам нужно обратиться к функции OptimalVideoCount
через метод feature
объекта Call
.
Затем вы можете установить слушатель через метод on
в OptimalVideoCountCallFeature
, чтобы получать уведомления при изменении optimalVideoCount. Чтобы отказаться от изменений, вы можете вызвать метод off
.
Текущее максимальное количество входящих видео, которые могут быть воспроизведены, составляет 16. Чтобы правильно поддерживать 16 входящих видео, компьютеру требуется не менее 16 ГБ ОЗУ и четыре (4) ядра или больше ЦП, который меньше трех (3) лет.
const optimalVideoCountFeature = call.feature(Features.OptimalVideoCount);
optimalVideoCountFeature.on('optimalVideoCountChanged', () => {
const localOptimalVideoCountVariable = optimalVideoCountFeature.optimalVideoCount;
})
Пример использования: приложение подписывается на изменения оптимального количества видео в групповых вызовах. Изменение оптимального количества видео обрабатывается путем создания нового метода отрисовки createView
или удаления представлений dispose
и обновления макета приложения соответствующим образом.
Свойства удаленного видеопотока
Удаленные видеопотоки имеют следующие свойства:
const id: number = remoteVideoStream.id;
-
id
: идентификатор удаленного видеопотока.
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
-
mediaStreamType
: Может бытьVideo
илиScreenSharing
.
const isAvailable: boolean = remoteVideoStream.isAvailable;
-
isAvailable
: Определяет, отправляет ли удаленный участник активно поток данных.
const isReceiving: boolean = remoteVideoStream.isReceiving;
isReceiving
:Сообщает приложению, принимаются ли данные удаленного видеопотока или нет.
Флаг перемещается к
false
в следующих случаях:- Удаленный участник, который использует мобильный браузер, сворачивает приложение браузера в фон.
- Удаленный участник или пользователь, получающий видео, имеет проблему с сетью, которая влияет на качество видео резко.
- Удаленный участник, работающий в macOS/iOS Safari, выбирает "Приостановить" в адресной строке.
- Удаленный участник имеет проблемы с соединением сети.
- Удаленный участник на мобильном устройстве закрывает или завершает браузер.
- Удаленный участник с мобильного или настольного устройства блокирует свое устройство. Этот сценарий также применяется, если удаленный участник находится на настольном компьютере и переходит в спящий режим.
Флаг перемещается к
true
в следующих случаях:- Удаленный участник, работающий в мобильном браузере и свернувший его, возвращает его на передний план.
- Удаленный участник, работающий в macOS/iOS Safari, выбирает "Возобновить" в адресной строке после приостановки видео.
- Удаленный участник повторно подключается к сети после временного отключения.
- Удаленный участник разблокирует свое мобильное устройство и возвращается к вызову через браузер на этом устройстве.
Эта функция улучшает впечатления пользователя при воспроизведении удаленных видеопотоков.
Вы можете отображать индикатор загрузки поверх удаленного видеопотока, когда флаг isReceiving изменяется на ложь. Вам не обязательно внедрять индикатор загрузки, но индикатор загрузки является самым распространенным решением для улучшения пользовательского опыта.
const size: StreamSize = remoteVideoStream.size;
size
: Размер потока с информацией о ширине и высоте видео.
Методы и свойства VideoStreamRenderer
await videoStreamRenderer.createView();
VideoStreamRendererView
Создайте экземпляр, который можно подключить в пользовательском интерфейсе приложения для отрисовки удаленного видеопотока, используйте асинхронный createView()
метод, он разрешает, когда поток готов к отрисовке и возвращает объект со target
свойством, представляющим video
элемент, который может быть вставлен в любое место в дереве DOM.
videoStreamRenderer.dispose();
Удалите videoStreamRenderer
и все связанные экземпляры VideoStreamRendererView
.
Методы и свойства VideoStreamRendererView
Когда вы создаёте VideoStreamRendererView
, вы можете указать свойства scalingMode
и isMirrored
.
scalingMode
может быть Stretch
, Crop
или Fit
. Если указано isMirrored
, поток рендеринга переворачивается по вертикали.
const videoStreamRendererView: VideoStreamRendererView = await videoStreamRenderer.createView({ scalingMode, isMirrored });
Каждая VideoStreamRendererView
экземпляр имеет свойство target
, которое представляет поверхность рендеринга. Подключите это свойство в пользовательском интерфейсе приложения:
htmlElement.appendChild(view.target);
Вы можете обновить scalingMode
, вызвав метод updateScalingMode
.
view.updateScalingMode('Crop');
Отправляйте видеопотоки с двух разных камер, в одном и том же звонке с одного и того же настольного устройства.
Важно
Эта функция Azure Communication Services в настоящее время находится в стадии предварительного просмотра. Функции в предварительной версии общедоступны и могут использоваться всеми новыми и существующими клиентами Майкрософт.
Предварительные API и SDK предоставляются без соглашения об уровне обслуживания. Мы рекомендуем не использовать их для рабочих нагрузок на производстве. Некоторые функции могут не поддерживаться или могут быть ограничены.
Для получения дополнительной информации см. Дополнительные условия использования для предварительных версий Microsoft Azure.
Поддержка отправки видеопотоков с двух различных камер в одном вызове доступна начиная с версии 1.17.1-beta.1+ в поддерживаемых браузерах на настольных компьютерах.
Вы можете отправлять видеопотоки с двух разных камер из одной вкладки браузера или приложения на настольном компьютере в одном звонке с помощью следующего фрагмента кода:
// Create your first CallAgent with identity A
const callClient1 = new CallClient();
const callAgent1 = await callClient1.createCallAgent(tokenCredentialA);
const deviceManager1 = await callClient1.getDeviceManager();
// Create your second CallAgent with identity B
const callClient2 = new CallClient();
const callAgent2 = await callClient2.createCallAgent(tokenCredentialB);
const deviceManager2 = await callClient2.getDeviceManager();
// Join the call with your first CallAgent
const camera1 = await deviceManager1.getCameras()[0];
const callObj1 = callAgent1.join({ groupId: ‘123’}, { videoOptions: { localVideoStreams: [new LocalVideoStream(camera1)] } });
// Join the same call with your second CallAgent and make it use a different camera
const camera2 = (await deviceManager2.getCameras()).filter((camera) => { return camera !== camera1 })[0];
const callObj2 = callAgent2.join({ groupId: '123' }, { videoOptions: { localVideoStreams: [new LocalVideoStream(camera2)] } });
//Mute the microphone and speakers of your second CallAgent’s Call, so that there is no echos/noises.
await callObj2.muteIncomingAudio();
await callObj2.mute();
Ограничения:
- Отправка видеопотоков должна выполняться двумя экземплярами под разными
CallAgent
идентификаторами. В фрагменте кода показаны два агентa вызова, каждый со своим собственным объектом вызова. - В примере кода оба CallAgent присоединяются к одному вызову (одинаковые идентификаторы вызовов). Вы также можете присоединиться к различным вызовам с каждым агентом и отправить одно видео в одном вызове и другое видео в другом вызове.
- Отправка одной и той же камеры в обоих CallAgents не поддерживается. Они должны быть двумя разными камерами.
- Отправка двух разных камер с одним CallAgent в настоящее время не поддерживается.
- В macOS Safari эффекты размытия фона для видео (из @azure/communication-effects)) могут быть применены только к одной камере, и не могут применяться одновременно к обеим.
Установите SDK
Найдите свой файл уровня проекта build.gradle
и добавьте mavenCentral()
в список репозиториев под buildscript
и allprojects
.
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Затем, в файле уровня модуля build.gradle
, добавьте следующие строки в раздел dependencies
:
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0'
...
}
Инициализируйте необходимые объекты
Чтобы создать CallAgent
экземпляр, необходимо вызвать метод createCallAgent
на экземпляре CallClient
. Этот вызов асинхронно возвращает объект CallAgent
экземпляра.
Метод createCallAgent
принимает CommunicationUserCredential
в качестве аргумента, который инкапсулирует маркер доступа.
Чтобы получить доступ к DeviceManager
, сначала необходимо создать экземпляр callAgent
. Затем можно использовать CallClient.getDeviceManager
метод для получения DeviceManager
.
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential).get();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
Чтобы задать отображаемое имя для вызывающего абонента, используйте этот альтернативный метод:
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgentOptions callAgentOptions = new CallAgentOptions();
callAgentOptions.setDisplayName("Alice Bob");
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential, callAgentOptions).get();
Управление устройствами
Чтобы использовать видео с вызовами, необходимо управлять устройствами. Использование устройств позволяет управлять передачей звука и видео в звонке.
Объект DeviceManager
позволяет перечислить локальные устройства, которые будут использоваться в вызове для передачи аудио-и видеопотоков. Он также позволяет запрашивать разрешение от пользователя на доступ к микрофону и камере с помощью API собственного браузера.
Чтобы получить доступ deviceManager
, вызовите callClient.getDeviceManager()
метод.
Context appContext = this.getApplicationContext();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
Перечислить локальные устройства
Для доступа к локальным устройствам используйте методы перечисления в диспетчере устройств. Перечисление — это синхронное действие.
// Get a list of available video devices for use.
List<VideoDeviceInfo> localCameras = deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
Локальный предварительный просмотр камеры
Вы можете использовать DeviceManager
и Renderer
, чтобы начать отображение потоков с вашей локальной камеры. Этот поток не отправляется другим участникам. Локальная лента предварительного просмотра. Отрисовка потока — это асинхронное действие.
VideoDeviceInfo videoDevice = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentVideoStream = new LocalVideoStream(videoDevice, appContext);
LocalVideoStream[] localVideoStreams = new LocalVideoStream[1];
localVideoStreams[0] = currentVideoStream;
VideoOptions videoOptions = new VideoOptions(localVideoStreams);
RenderingOptions renderingOptions = new RenderingOptions(ScalingMode.Fit);
VideoStreamRenderer previewRenderer = new VideoStreamRenderer(currentVideoStream, appContext);
VideoStreamRendererView uiView = previewRenderer.createView(renderingOptions);
// Attach the uiView to a viewable location on the app at this point
layout.addView(uiView);
Проведите личный звонок с видеокамерой
Предупреждение
В настоящее время поддерживается только один исходящий локальный видеопоток. Чтобы совершить видеозвонок, необходимо перечислить локальные камеры с помощью deviceManager
getCameras
API.
После выбора камеры используйте её для создания экземпляра LocalVideoStream
и передачи этого экземпляра в метод videoOptions
в виде элемента массива localVideoStream
. После подключения вызов автоматически начинает отправлять видеопоток с выбранной камеры другим участникам.
Примечание
Из-за проблем с конфиденциальностью видео не передается в вызов, если оно не предварительно просмотрено локально. Дополнительные сведения см. в предварительной версии локальной камеры.
VideoDeviceInfo desiredCamera = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentVideoStream = new LocalVideoStream(desiredCamera, appContext);
LocalVideoStream[] localVideoStreams = new LocalVideoStream[1];
localVideoStreams[0] = currentVideoStream;
VideoOptions videoOptions = new VideoOptions(localVideoStreams);
// Render a local preview of video so the user knows that their video is being shared
Renderer previewRenderer = new VideoStreamRenderer(currentVideoStream, appContext);
View uiView = previewRenderer.createView(new CreateViewOptions(ScalingMode.FIT));
// Attach the uiView to a viewable location on the app at this point
layout.addView(uiView);
CommunicationUserIdentifier[] participants = new CommunicationUserIdentifier[]{ new CommunicationUserIdentifier("<acs user id>") };
StartCallOptions startCallOptions = new StartCallOptions();
startCallOptions.setVideoOptions(videoOptions);
Call call = callAgent.startCall(context, participants, startCallOptions);
Начать и остановить передачу локального видео.
Чтобы запустить видео, необходимо перечислить камеры с помощью операции getCameraList
на объекте deviceManager
. Затем создайте новый экземпляр LocalVideoStream
, передав желаемую камеру, и используйте его в API startVideo
в качестве аргумента.
VideoDeviceInfo desiredCamera = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentLocalVideoStream = new LocalVideoStream(desiredCamera, appContext);
VideoOptions videoOptions = new VideoOptions(currentLocalVideoStream);
Future startVideoFuture = call.startVideo(appContext, currentLocalVideoStream);
startVideoFuture.get();
После того как вы успешно начнёте отправку видео, экземпляр LocalVideoStream
добавляется в коллекцию localVideoStreams
в контексте вызова.
List<LocalVideoStream> videoStreams = call.getLocalVideoStreams();
LocalVideoStream currentLocalVideoStream = videoStreams.get(0); // Please make sure there are VideoStreams in the list before calling get(0).
Чтобы остановить локальное видео, передайте экземпляр LocalVideoStream
, доступный в коллекции localVideoStreams
.
call.stopVideo(appContext, currentLocalVideoStream).get();
Вы можете переключиться на другое устройство камеры во время отправки видео, вызвав switchSource
на экземпляре LocalVideoStream
.
currentLocalVideoStream.switchSource(source).get();
Показ видеопотоков удаленных участников
Чтобы перечислить видеопотоки и потоки демонстрации экрана удаленных участников, просмотрите коллекции videoStreams
:
List<RemoteParticipant> remoteParticipants = call.getRemoteParticipants();
RemoteParticipant remoteParticipant = remoteParticipants.get(0); // Please make sure there are remote participants in the list before calling get(0).
List<RemoteVideoStream> remoteStreams = remoteParticipant.getVideoStreams();
RemoteVideoStream remoteParticipantStream = remoteStreams.get(0); // Please make sure there are video streams in the list before calling get(0).
MediaStreamType streamType = remoteParticipantStream.getType(); // of type MediaStreamType.Video or MediaStreamType.ScreenSharing
Чтобы отобразить RemoteVideoStream
от удаленного участника, вам нужно подписаться на событие OnVideoStreamsUpdated
.
В пределах события изменение свойства isAvailable
на true указывает, что удалённый участник в данный момент отправляет поток. Как только это произойдёт, создайте новый экземпляр Renderer
, затем создайте новый RendererView
, используя асинхронный API createView
и прикрепите view.target
в любом месте UI вашего приложения.
Всякий раз, когда доступность удаленного потока изменяется, вы можете либо уничтожить все Renderer
, либо определить RendererView
, либо сохранить их, что приведёт к отображению пустого кадра видео.
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteParticipantStream, appContext);
VideoStreamRendererView uiView = remoteVideoRenderer.createView(new RenderingOptions(ScalingMode.FIT));
layout.addView(uiView);
remoteParticipant.addOnVideoStreamsUpdatedListener(e -> onRemoteParticipantVideoStreamsUpdated(p, e));
void onRemoteParticipantVideoStreamsUpdated(RemoteParticipant participant, RemoteVideoStreamsEvent args) {
for(RemoteVideoStream stream : args.getAddedRemoteVideoStreams()) {
if(stream.getIsAvailable()) {
startRenderingVideo();
} else {
renderer.dispose();
}
}
}
Свойства удаленного видеопотока
Удаленный видеопоток имеет следующие свойства:
Id
— идентификатор удаленного видеопотока.int id = remoteVideoStream.getId();
MediaStreamType
— Может бытьVideo
илиScreenSharing
.MediaStreamType type = remoteVideoStream.getMediaStreamType();
isAvailable
— указывает, активно ли конечная точка удаленного участника отправляет поток.boolean availability = remoteVideoStream.isAvailable();
Методы и свойства рендерера
Объект Renderer
использует следующие методы.
Чтобы отобразить удаленный видеопоток, создайте экземпляр
VideoStreamRendererView
, который потом можно будет прикрепить к пользовательскому интерфейсу приложения.// Create a view for a video stream VideoStreamRendererView.createView()
Удалите рендерер и все
VideoStreamRendererView
, связанные с этим рендерером. Вызовите его после удаления всех связанных представлений из пользовательского интерфейса.VideoStreamRenderer.dispose()
Чтобы задать размер (ширина и высота) удаленного видеопотока, используйте
StreamSize
.StreamSize renderStreamSize = VideoStreamRenderer.getSize(); int width = renderStreamSize.getWidth(); int height = renderStreamSize.getHeight();
методы и свойства RendererView
При создании VideoStreamRendererView
можно указать свойства ScalingMode
и mirrored
, которые применяются к этому представлению.
Режим масштабирования может быть либо CROP
, либо FIT
.
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteVideoStream, appContext);
VideoStreamRendererView rendererView = remoteVideoRenderer.createView(new CreateViewOptions(ScalingMode.Fit));
Созданный RendererView затем можно подключить к пользовательскому интерфейсу приложения, используя следующий фрагмент кода:
layout.addView(rendererView);
Позже можно обновить режим масштабирования с помощью операции на объекте updateScalingMode
с аргументом RendererView
или ScalingMode.CROP
.
// Update the scale mode for this view.
rendererView.updateScalingMode(ScalingMode.CROP)
Настройте вашу систему
Следуйте этим шагам, чтобы настроить вашу систему.
Создайте проект Xcode
В Xcode создайте новый проект iOS и выберите шаблон Single View App. В этой статье используется платформа SwiftUI, поэтому необходимо задать для языка значение Swift и задать для интерфейсазначение SwiftUI.
Вы не будете создавать тесты в этой статье. Вы можете снять галочку с поля Include Tests.
Установите пакет и зависимости с помощью CocoaPods.
Создайте Podfile для вашего приложения, как в этом примере:
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
Выполните
pod install
.Откройте
.xcworkspace
с помощью Xcode.
Запросить доступ к микрофону
Чтобы получить доступ к микрофону устройства, вам нужно обновить список свойств информации вашего приложения, используя NSMicrophoneUsageDescription
. Задайте связанное значение строке, включенной в диалоговое окно, которое система использует для запроса доступа от пользователя.
Щелкните правой кнопкой мыши на элементе Info.plist в структуре проекта, затем выберите Открыть как>исходный код. Добавьте в раздел верхнего уровня <dict>
следующие строки, а затем сохраните файл.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
Настройте структуру приложения
Откройте файл ContentView.swift
вашего проекта. Добавьте объявление import
в начало файла для импорта библиотеки AzureCommunicationCalling
. Кроме того, импортируйте AVFoundation
. Он необходим для запросов на разрешение звука в коде.
import AzureCommunicationCalling
import AVFoundation
Инициализация CallAgent
Чтобы создать экземпляр CallAgent
из CallClient
, необходимо использовать метод callClient.createCallAgent
, который асинхронно возвращает объект CallAgent
после его инициализации.
Чтобы создать клиента вызова, передайте объект CommunicationTokenCredential
:
import AzureCommunication
let tokenString = "token_string"
var userCredential: CommunicationTokenCredential?
do {
let options = CommunicationTokenRefreshOptions(initialToken: token, refreshProactively: true, tokenRefresher: self.fetchTokenSync)
userCredential = try CommunicationTokenCredential(withOptions: options)
} catch {
updates("Couldn't created Credential object", false)
initializationDispatchGroup!.leave()
return
}
// tokenProvider needs to be implemented by Contoso, which fetches a new token
public func fetchTokenSync(then onCompletion: TokenRefreshOnCompletion) {
let newToken = self.tokenProvider!.fetchNewToken()
onCompletion(newToken, nil)
}
Передайте объект CommunicationTokenCredential
, который вы создали, в CallClient
, и установите отображаемое имя.
self.callClient = CallClient()
let callAgentOptions = CallAgentOptions()
options.displayName = " iOS Azure Communication Services User"
self.callClient!.createCallAgent(userCredential: userCredential!,
options: callAgentOptions) { (callAgent, error) in
if error == nil {
print("Create agent succeeded")
self.callAgent = callAgent
} else {
print("Create agent failed")
}
})
Управлять устройствами
Чтобы начать использовать видео с вызовами, необходимо знать, как управлять устройствами. Устройства позволяют вам контролировать передачу звука и видео в вызов.
DeviceManager
позволяет перечислять локальные устройства, которые можно использовать в вызове для передачи аудио- или видеопотоков. Он также позволяет запрашивать разрешение от пользователя на доступ к микрофону или камере. Вы можете получить доступ к deviceManager
на объекте callClient
.
self.callClient!.getDeviceManager { (deviceManager, error) in
if (error == nil) {
print("Got device manager instance")
self.deviceManager = deviceManager
} else {
print("Failed to get device manager instance")
}
}
Перечислить локальные устройства
Для доступа к локальным устройствам можно использовать методы перечисления в диспетчере устройств. Перечисление — это синхронное действие.
// enumerate local cameras
var localCameras = deviceManager.cameras // [VideoDeviceInfo, VideoDeviceInfo...]
Получить локальный предварительный просмотр камеры
Вы можете использовать Renderer
, чтобы начать передачу потока с вашей локальной камеры. Этот поток не передается другим участникам; это локальный предварительный поток. Это асинхронное действие.
let camera: VideoDeviceInfo = self.deviceManager!.cameras.first!
let localVideoStream = LocalVideoStream(camera: camera)
let localRenderer = try! VideoStreamRenderer(localVideoStream: localVideoStream)
self.view = try! localRenderer.createView()
Получение свойств предварительного просмотра локальной камеры
Отрисовщик включает набор свойств и методов, позволяющих управлять отрисовкой.
// Constructor can take in LocalVideoStream or RemoteVideoStream
let localRenderer = VideoStreamRenderer(localVideoStream:localVideoStream)
let remoteRenderer = VideoStreamRenderer(remoteVideoStream:remoteVideoStream)
// [StreamSize] size of the rendering view
localRenderer.size
// [VideoStreamRendererDelegate] an object you provide to receive events from this Renderer instance
localRenderer.delegate
// [Synchronous] create view
try! localRenderer.createView()
// [Synchronous] create view with rendering options
try! localRenderer!.createView(withOptions: CreateViewOptions(scalingMode: ScalingMode.fit))
// [Synchronous] dispose rendering view
localRenderer.dispose()
Совершите личный звонок один на один с видео
Чтобы получить экземпляр диспетчера устройств, см. раздел о управлении устройствами.
let firstCamera = self.deviceManager!.cameras.first
self.localVideoStreams = [LocalVideoStream]()
self.localVideoStreams!.append(LocalVideoStream(camera: firstCamera!))
let videoOptions = VideoOptions(localVideoStreams: self.localVideoStreams!)
let startCallOptions = StartCallOptions()
startCallOptions.videoOptions = videoOptions
let callee = CommunicationUserIdentifier('UserId')
self.callAgent?.startCall(participants: [callee], options: startCallOptions) { (call, error) in
if error == nil {
print("Successfully started outgoing video call")
self.call = call
} else {
print("Failed to start outgoing video call")
}
}
Показ видеопотоков удаленных участников
Удаленные участники могут инициировать видео или совместное использование экрана во время звонка.
Обрабатывайте потоки видео- или экранного обмена удаленных участников.
Чтобы перечислить потоки удаленных участников, проверьте коллекции videoStreams
.
var remoteParticipantVideoStream = call.remoteParticipants[0].videoStreams[0]
Получить свойства удалённого видеопотока.
var type: MediaStreamType = remoteParticipantVideoStream.type // 'MediaStreamTypeVideo'
var isAvailable: Bool = remoteParticipantVideoStream.isAvailable // indicates if remote stream is available
var id: Int = remoteParticipantVideoStream.id // id of remoteParticipantStream
Отображение потоков удаленных участников
Чтобы начать рендеринг потоков удалённых участников, используйте следующий код.
let renderer = VideoStreamRenderer(remoteVideoStream: remoteParticipantVideoStream)
let targetRemoteParticipantView = renderer?.createView(withOptions: CreateViewOptions(scalingMode: ScalingMode.crop))
// To update the scaling mode later
targetRemoteParticipantView.update(scalingMode: ScalingMode.fit)
Получение методов и свойств удалённого видеоренедера.
// [Synchronous] dispose() - dispose renderer and all `RendererView` associated with this renderer. To be called when you have removed all associated views from the UI.
remoteVideoRenderer.dispose()
Настройте вашу систему
Следуйте этим шагам, чтобы настроить вашу систему.
Создание проекта Visual Studio
В Visual Studio 2022 для приложения универсальной платформы Windows создайте новый проект Пустое приложение (универсальная платформа Windows). После ввода имени проекта вы можете свободно выбрать любую версию Windows SDK не ниже 10.0.17763.0.
Для приложения WinUI 3 создайте новый проект, используя шаблон Blank App, Packaged (WinUI 3 in Desktop), чтобы настроить одностраничное приложение WinUI 3. Для работы требуется Windows App SDK версии 1.3 или более поздней.
Установите пакет и зависимости с помощью диспетчера пакетов NuGet.
API и библиотеки SDK Calling доступны публично через пакет NuGet.
Чтобы найти, загрузить и установить пакет NuGet SDK для вызовов:
- Откройте диспетчер пакетов NuGet, выбрав Инструменты>Диспетчер пакетов NuGet>Управление пакетами NuGet для решения.
- Выберите Обзор, затем введите Azure.Communication.Calling.WindowsClient в строке поиска.
- Убедитесь, что флажок Include prerelease установлен.
- Выберите пакет Azure.Communication.Calling.WindowsClient, а затем выберите Azure.Communication.Calling.WindowsClient1.4.0-beta.1 или более новую версию.
- Выберите флажок, который соответствует проекту Azure Communication Services в правой панели.
- Нажмите Установить.
Запросить доступ к микрофону
Приложению требуется доступ к камере. В приложениях универсальной платформы Windows (UWP) необходимо объявить возможность камеры в файле манифеста приложения.
- Откройте проект в Visual Studio.
- На панели обозревателя решений дважды щелкните файл с
.appxmanifest
расширением. - Щелкните вкладку "Возможности".
- Выберите флажок
Camera
из списка возможностей.
Создайте элементы интерфейса для установки и завершения вызова.
Этот пример приложения содержит две кнопки. Одна кнопка для набора вызова и другая для завершения вызова.
- На панели обозревателя решений дважды щелкните файл с именем
MainPage.xaml
UWP илиMainWindows.xaml
WinUI 3. - В центральной панели найдите код XAML под предварительным просмотром интерфейса.
- Измените код XAML с помощью следующего фрагмента:
<TextBox x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" />
<StackPanel>
<Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" />
<Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" />
</StackPanel>
Настройка приложения с помощью API SDK для вызовов
API вызовов SDK находятся в двух разных пространствах имен.
Выполните следующие действия, чтобы сообщить компилятору C# об этих пространствах имен, что позволяет Intellisense Visual Studio помочь в разработке кода.
- На панели обозревателя решений щелкните стрелку в левой части файла с именем
MainPage.xaml
UWP илиMainWindows.xaml
WinUI 3. - Дважды щелкните по файлу с именем
MainPage.xaml.cs
илиMainWindows.xaml.cs
. - Добавьте следующие команды в конце текущих операторов
using
.
using Azure.Communication.Calling.WindowsClient;
Оставьте MainPage.xaml.cs
или MainWindows.xaml.cs
открытым. На следующем шаге добавляется дополнительный код.
Включение взаимодействия с приложением
Добавленные кнопки пользовательского интерфейса должны работать поверх размещенного элемента CommunicationCall
. Это означает, что необходимо добавить CommunicationCall
поле данных в MainPage
класс или MainWindow
класс.
Кроме того, необходимо включить асинхронную операцию, чтобы создание CallAgent
прошло успешно.
CallAgent
Добавьте член данных в тот же класс.
Добавьте следующие члены данных в класс MainPage
или MainWindow
.
CallAgent callAgent;
CommunicationCall call;
Создать обработчики кнопок
Ранее мы добавили две кнопки пользовательского интерфейса в код XAML. Следующий код добавляет обработчики для запуска при нажатии кнопки.
Добавьте следующий код после элементов данных из предыдущего раздела.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// End the current call
}
Объектная модель
Следующие классы и интерфейсы реализуют некоторые основные функции клиентской библиотеки вызовов в Службах коммуникации Azure для UWP:
Имя | Описание |
---|---|
CallClient |
CallClient является главным входом в библиотеку клиента Calling. |
CallAgent |
CallAgent используется для начала и присоединения к звонкам. |
CommunicationCall |
CommunicationCall используется для управления выполненными или принятыми вызовами. |
CommunicationTokenCredential |
Для создания экземпляра CommunicationTokenCredential используется CallAgent в качестве учетных данных токена. |
CallAgentOptions |
Элемент CallAgentOptions содержит информацию для идентификации вызывающего абонента. |
HangupOptions |
HangupOptions сообщает, должен ли вызов быть завершён всем своим участникам. |
Зарегистрировать обработчик схемы видео
Компонент пользовательского интерфейса, например XAML MediaElement
или MediaPlayerElement
, требует от приложения регистрации конфигурации для отрисовки локальных и удаленных видеопотоков.
Добавьте следующий контент между тегами Package
внутри Package.appxmanifest
:
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>RtmMvrUap.dll</Path>
<ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
Инициализировать CallAgent
Чтобы создать экземпляр CallAgent
из CallClient
, необходимо использовать метод CallClient.CreateCallAgentAsync
, который асинхронно возвращает объект CallAgent
после его инициализации.
Чтобы создать CallAgent
, вы должны передать объект CallTokenCredential
и объект CallAgentOptions
. Помните, что CallTokenCredential
вызывает исключение при передаче некорректного маркера.
Добавьте следующий код внутрь вспомогательной функции, чтобы она выполнялась во время инициализации.
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.CallsUpdated += Agent_OnCallsUpdatedAsync;
this.callAgent.IncomingCallReceived += Agent_OnIncomingCallAsync;
Замените <AUTHENTICATION_TOKEN>
на действительный токен учетных данных для вашего ресурса. Дополнительные сведения о получении токена учетных данных см. в разделе токена доступа пользователя.
Проведите личный звонок с видеокамерой
Объекты, необходимые для создания CallAgent
, теперь готовы. Затем асинхронно создайте CallAgent
и поместите видеозвонок.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
if (!string.IsNullOrEmpty(callString))
{
if (callString.StartsWith("8:")) // 1:1 Azure Communication Services call
{
this.call = await StartAcsCallAsync(callString);
}
}
if (this.call != null)
{
this.call.RemoteParticipantsUpdated += OnRemoteParticipantsUpdatedAsync;
this.call.StateChanged += OnStateChangedAsync;
}
}
private async Task<CommunicationCall> StartAcsCallAsync(string acsCallee)
{
var options = await GetStartCallOptionsAsync();
var call = await this.callAgent.StartCallAsync( new [] { new UserCallIdentifier(acsCallee) }, options);
return call;
}
var micStream = new LocalOutgoingAudioStream(); // Create a default local audio stream
var cameraStream = new LocalOutgoingVideoStream(this.viceManager.Cameras.FirstOrDefault() as VideoDeviceDetails); // Create a default video stream
private async Task<StartCallOptions> GetStartCallOptionsAsync()
{
return new StartCallOptions() {
OutgoingAudioOptions = new OutgoingAudioOptions() { IsMuted = true, Stream = micStream },
OutgoingVideoOptions = new OutgoingVideoOptions() { Streams = new OutgoingVideoStream[] { cameraStream } }
};
}
Локальный предварительный просмотр камеры
При желании мы можем настроить локальный предварительный просмотр камеры. Видео можно отобразить с помощью MediaPlayerElement
:
<Grid>
<MediaPlayerElement x:Name="LocalVideo" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" AutoPlay="True" />
</Grid>
Чтобы инициализировать локальный предварительный просмотр MediaPlayerElement
:
private async void CameraList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cameraStream != null)
{
await cameraStream?.StopPreviewAsync();
if (this.call != null)
{
await this.call?.StopVideoAsync(cameraStream);
}
}
var selectedCamera = CameraList.SelectedItem as VideoDeviceDetails;
cameraStream = new LocalOutgoingVideoStream(selectedCamera);
var localUri = await cameraStream.StartPreviewAsync();
LocalVideo.Source = MediaSource.CreateFromUri(localUri);
if (this.call != null) {
await this.call?.StartVideoAsync(cameraStream);
}
}
Отображение потокового видео с удалённой камеры
Настройте обработчик событий в ответ на событие OnCallsUpdated
:
private async void 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 void OnRemoteParticipantsUpdatedAsync(object sender, ParticipantsUpdatedEventArgs args)
{
await OnParticipantChangedAsync(
args.RemovedParticipants.ToList<RemoteParticipant>(),
args.AddedParticipants.ToList<RemoteParticipant>());
}
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;
}
}
Начало воспроизведения удаленного видеопотока на MediaPlayerElement
:
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);
});
/* Or WinUI 3
this.DispatcherQueue.TryEnqueue(() => {
RemoteVideo.Source = MediaSource.CreateFromUri(uri);
RemoteVideo.MediaPlayer.Play();
});
*/
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;
}
}
Завершение вызова
После установления вызова используйте HangupAsync
метод объекта CommunicationCall
для завершения вызова.
Используйте экземпляр HangupOptions
для информирования участников о необходимости завершения вызова.
Добавьте в нее следующий код HangupButton_Click
.
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
foreach (var localVideoStream in call.OutgoingVideoStreams)
{
await call.StopVideoAsync(localVideoStream);
}
try
{
if (cameraStream != null)
{
await cameraStream.StopPreviewAsync();
}
await call.HangUpAsync(new HangUpOptions() { ForEveryone = false });
}
catch(Exception ex)
{
var errorCode = unchecked((int)(0x0000FFFFU & ex.HResult));
if (errorCode != 98) // Sample error code, sam_status_failed_to_hangup_for_everyone (98)
{
throw;
}
}
}
}
Запустите код
- Убедитесь, что Visual Studio создает приложение для
x64
,x86
илиARM64
. - Нажмите клавишу F5 , чтобы начать запуск приложения.
- Нажмите кнопку CommunicationCall , чтобы отправить вызов определенному получателю.
При первом запуске приложения система предложит пользователю предоставить доступ к микрофону.