Во всех наших приложениях сотрудники могут общаться при помощи встроенного чата. При этом все чаще можно увидеть приложения, которые позволяют общаться пользователям по видеосвязи без перехода в сторонние сервисы.
В этой статье я расскажу про удобный способ создания такого функционала. Расскажу про готовые инструменты, опишу основные моменты, мысли, ссылки. Будет интересно!
![](https://habrastorage.org/getpro/habr/upload_files/dfc/625/6c5/dfc6256c505e199e141dad2c29dc94c3.png)
Что такое 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);
}
Результат работы программы
![Подключение участников к видеозвонку Angular/Openvidu Подключение участников к видеозвонку Angular/Openvidu](https://habrastorage.org/getpro/habr/upload_files/6bd/5b3/8f7/6bd5b38f7b1acde0d3c3ed7cf5a4846b.gif)
Демонстрация экрана участником
Демонстрация очень похожа на инициализацию камеры за исключением того что в качестве видеоисточника указывается '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);
}
Результат работы
![Демонстрация экрана Angular/openvidu Демонстрация экрана Angular/openvidu](https://habrastorage.org/getpro/habr/upload_files/21e/727/9c8/21e7279c8c2624c12f314103d282bcc1.gif)
Выключение/включение камеры/звука участниками
Для того чтобы отключить или включить видео/звук нужно воспользоваться следующими методами у паблишера
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 вы можете улучшить пользовательский опыт ваших клиентов, дав им возможность звонить из приложений.