Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Во всех наших приложениях сотрудники могут общаться при помощи встроенного чата. При этом все чаще можно увидеть приложения, которые позволяют общаться пользователям по видеосвязи без перехода в сторонние сервисы.
В этой статье я расскажу про удобный способ создания такого функционала. Расскажу про готовые инструменты, опишу основные моменты, мысли, ссылки. Будет интересно!
Что такое WebRTC
Web Real Time Communications - это технология, которая позволяет осуществлять передачу аудио и видео потоков. На базе нее строятся приложения для видеконференций, например, Google meet, также ее применяют в приложениях для совместой работы (miro) и социальных сетях (vk). Подробно описывать WebRTC не будем, на эту тему полно статей и документации, сосредоточемся на технологии Angular.
Openvidu
Это платформа для создания видеозвонков основанная на webrtc. Она включает в себя бекенд реализацию и клиентские библиотеки. Мы сосредоточимся на клиенте так как статья посвящена Angular.
Приведем возможности клиентской бибилиотеки Openvidu:
Осуществлять аудио/видео звонки
Демонстрировать экран
Поддержка чата
Возможность переключения устройств ввода/вывода
Определение речи
Кастомизация клиентских библиотек
Многие возможности мы рассмотрим на примерах ниже.
Основные объекты из API Openvidu для построения приложения:
OpenVidu - класс отвечающий за основную точку входа для работы с webRtc
Session - представляет из себя реализацию звонков. По сути комната в рамках которой участники конференции могут взаимодействовать
Publisher - описывает участника сессии, который стримит аудио/видео поток
Subscriber - описывает участника, который может получать аудио/видео поток от Publisher в рамках сессии
Подготовка окружения
Для демо приложения воспользуемся готовым docker контейнером, который содержит в себе сервер openvidu. Документацию для установки можно найти по ссылке.
Предпологается, что у вас установлен docker
Для запуска контейнера необходимо выполнить следующую команду.
docker run -p 4443:4443 --rm -e OPENVIDU_SECRET=MY_SECRET openvidu/openvidu-server-kms:2.20.0
Также для разработки и тестирования могут потребоваться виртуальные камеры. Можно рассмотреть следущие:
SplitCam
ManyCam
С помощью них можно подставить любое видео в качестве видеопотока для приложения. А также API браузера сможет определять виртуальные устройства в качестве источника.
Эти два простых шага достаточны для создания клиентского демо приложения. Приступим!
Создание приложения
Для примера создадим приложение для совещаний, в котором будут находиться N пользователей. Каждый из них будет иметь возможность совершать базовые действия в приложении:
Подключаться к конференции
Включать/выключать видео/аудио
Демонстрировать экран
Идентифицировать говорящего
Подключение участников к видеоконференции
Создадим video-call.service.ts, который будет отвечать за работу с OpenVidu и упралять логикой видеозвонка. Для начала реализуем подключение к серверу и создание сессии join:
join() {
this.session = this.OV.initSession();
this.subscribeSessionEvents();
from(this.getToken())
.pipe(
switchMap((token: string) => {
return from(this.session.connect(token, { clientData: this.userName }))
}),
tap(() => {
this.initPublisher();
})
).subscribe();
}
В этом методе происходит следующее:
создание объекта сессии
подписка на события сессии
получение токена с сервера
подключение к сессии видеозвонка
создание паблишера
Рассмотрим подробнее создание паблишера:
initPublisher() {
const publisher: Publisher = this.OV.initPublisher('', {
audioSource: undefined,
videoSource: this.deviceId,
publishAudio: true,
publishVideo: true,
resolution: '640x480',
frameRate: 30,
insertMode: 'APPEND',
mirror: false
});
this.session.publish(publisher);
this.currentUser$.next(publisher);
}
initPublisher создает инстанс паблишера в который передаются настройки:
audioSource/videoSource - источники звука и видео. Это могут быть все возможные физические и виртуальные камеры и микрофоны
publishAudio/publishVideo - состояние публикации звука или видео
а также различные настройки качества и разрешения видео
Для трансляции видео нужно сделать компонент в котором будет связываться элемент video и видеопоток.
Шаблон компонента, который содержит тег video
<div class="ov-video">
<video #videoElement></video>
</div>
Код компонента. В нем происходит получение ссылки на video элемент и при помощи API openvidu и метода addVideoElement видеопоток привязвается к html.
@ViewChild('videoElement') elementRef: ElementRef | undefined;
_streamManager: StreamManager;
ngAfterViewInit() {
this._streamManager.addVideoElement(this.elementRef.nativeElement);
}
Результат работы программы
Демонстрация экрана участником
Демонстрация очень похожа на инициализацию камеры за исключением того что в качестве видеоисточника указывается 'screen'
initScreenPublisher() {
const publisher: Publisher = this.OV.initPublisher('', {
audioSource: undefined,
videoSource: 'screen',
publishAudio: true,
publishVideo: true,
resolution: '640x480',
frameRate: 30,
insertMode: 'APPEND',
mirror: false
});
this.session.publish(publisher);
this.currentUser$.next(publisher);
}
Результат работы
Выключение/включение камеры/звука участниками
Для того чтобы отключить или включить видео/звук нужно воспользоваться следующими методами у паблишера
enableVideo() {
this.currentUser$.value.publishVideo(true);
this.videoEnabled = true;
}
disableVideo() {
this.currentUser$.value.publishVideo(false);
this.videoEnabled = false;
}
Подсветка говорящего участника с помощью API определения речи
В начале создания приложения мы вызывали subscribeSessionEvents, настало время его рассмотреть подробнее:
subscribeSessionEvents() {
this.session.on('streamCreated', (event: any) => {
const subscriber: Subscriber = this.session.subscribe(event.stream, '');
this.users$.value.push(subscriber);
this.users$.next([...this.users$.value]);
});
this.session.on('streamDestroyed', (event: any) => {
// Удалить пользователя с конференции
});
this.session.on('publisherStartSpeaking', (event: any) => {
// пробросить событие и подстветить юзера с event.connection.connectionId
});
this.session.on('publisherStopSpeaking', (event: any) => {
// пробросить событие и подстветить
});
}
Сессия сама предоставляет нам информацию об участнике который начинает и заканчивает говорить
publisherStartSpeaking - участник начинает говорить в объекте event содержится идентификатор участника, имея эту информацию можно выдеделить компонент этого участника
publisherStopSpeaking - по этому событию можно снять выделение участника
Переключение камеры
Есть два сценария выбора камеры
при инициализации паблишера
при переключении во время публикации
Для первого варианта необходимо получать информацию о текущих девайсах устройства, для этого можно воспользоваться как стандартными браузерными API так и оберткой из OpenVidu
getDevices() {
from(this.OV.getDevices())
.pipe(
tap(x => {
const videoDevices = x.filter(x => x.kind === 'videoinput');
.....
})
).subscribe();
}
Устройства можно вывести в выпадающий список в интерфейсе и при выборе его устанавливать значение deviceId в паблишер (методы выше initPublisher)
Для того, чтобы переключать камеры в режиме реального времени, нужно получить mediaStream из выбранного устройства
selectCamera(value) {
this.videoCallService.getVideoMediaTrack({videoSource: value}).pipe(
untilDestroyed(this),
tap(mediaTrack => {
if (mediaTrack) {
this.videoCallService.videoMediaTrack$.next(mediaTrack);
}
}
)).subscribe();
}
Далее воспользуемся API:
replaceTrack(track: MediaStreamTrack) {
this.publisher.replaceTrack(track).then(() => ...
.catch(error => console.error('Error replacing track'));
}
На этом этапе мы научились делать весь фукнкционал который обозначили в начале статьи
Рекомендации, решения, мысли, ссылки
В Chrome существует особая политика с автовоспроизведением видео. Ее стоит учитывать, так как существуют сценарии когда видеопоток не будет стартовать, например пока пользователь не сделает какое-либо действие на странице.
Помимо трансляции видео с камеры( виртуальной камеры) или рабочего стола, можно транслировать обычное видео с помощью API браузера - captureStream
Документация MediaStream - в ней можно узнать детали потоков медиаданных
Для включения видеоэкрана "во весь экран" можно воспользоваться API requestFullscreen
В одной из статей я писал про технологию Web Speech API. Интересно можно ли ее применить для WebRTC, например, для преобразования аудиопотока в текст прямо в браузере. Был ли у вас такой опыт?
Ссылка на пример из статьи
У ZOOM есть свой sdk для построения подобных приложений, интересны его возможности
Заключение
Сейчас все больше приложений позволяют осуществлять видеозвонки - от социальных сетей до приложений для совместной работы. С помощью технологии WebRTC вы можете улучшить пользовательский опыт ваших клиентов, дав им возможность звонить из приложений.