Делаем видеозвонки с помощью Angular, WebRTC и Openvidu

от автора

Во всех наших приложениях сотрудники могут общаться при помощи встроенного чата. При этом все чаще можно увидеть приложения, которые позволяют общаться пользователям по видеосвязи без перехода в сторонние сервисы.

В этой статье я расскажу про удобный способ создания такого функционала. Расскажу про готовые инструменты, опишу основные моменты, мысли, ссылки. Будет интересно!

Что такое 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

Демонстрация экрана участником

Демонстрация очень похожа на инициализацию камеры за исключением того что в качестве видеоисточника указывается ‘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

Выключение/включение камеры/звука участниками

Для того чтобы отключить или включить видео/звук нужно воспользоваться следующими методами у паблишера

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 вы можете улучшить пользовательский опыт ваших клиентов, дав им возможность звонить из приложений.


ссылка на оригинал статьи https://habr.com/ru/company/europlan/blog/595731/