Pull to refresh

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

Reading time6 min
Views7.4K

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

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

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

Tags:
Hubs:
Total votes 7: ↑7 and ↓0+7
Comments2

Articles

Information

Website
europlan.ru
Registered
Employees
1,001–5,000 employees
Location
Россия