Как мы делали приложение а-ля Google Meet с помощью PeerJS, SocketIO и NextJS

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет, дорогие читатели Хабр. Мы долго думали, чтобы нам сделать такое, что от нас не потребует глубоких знаний бэкенда и базы данных, но все же интересное и обучающее, исключительно ориентированное на конечного пользователя. Так мы пришли к тому, что нам бы хотелось изучить более подробно сферу WebRTC и WebSockets и решили сделать что-то похожее на Google Meet c еë основными фичами, которые более подробно описаны чуть ниже. Но давайте все по порядку :) Приготовьтесь, будет много кода!

Стек технологий

Для клиентской части мы выбрали NextJS, этот фреймворк из коробки даёт нам возможность создать "endpoint handler" в два щелчка (это нам понадобится для SocketIO), плюс супер легко настраивать навигацию по страницам.

Для стилизации, наш выбор пал на TailwindCSS, потому что мы с ним никогда раньше не работали и нам хотелось попробовать.

Прочитав пару тройку статей и просмотрев несколько видео, мы узнали, что WebRTC в чистом виде является громоздким и неповоротливым. В связи с этим перед нами стоял выбор между библиотеками PeerJS и Simple-Peer. Поэкспериментировав обеими библиотеками, для проекта мы остановились на PeerJS. PeerJS покрывает все настройки по умолчанию такие как STUN server, ICE candidate, так что можно не беспокоиться насчëт этого. Для нас это было супер решением из-за наших скудных знаний в этой области.

Более подробно о протоколах WebRTC можете изучить здесь.

Когда вы работаете с WebRTC технологией, у вас также должен быть Signaling Server в роли синхронизатора. Этот сервер поможет вам держать всех участников обновлёнными и реагировать соответственно в случае тех или иных событий пользователя. В качестве Signaling Server у нас служит SocketIO.

Для полного антуража, мы также подкрутили базовую авторизацию на платформе Auth0.

Фичи

Так какие же фичи нас ждут в этом приложении:

  • страница-лобби для первоначальных настроек перед входом в комнату

  • создание комнаты

  • особые возможности организатора комнаты

  • демонстрация экрана

  • отключение светового индикатора при выключении камеры

  • индикатор активного спикера

  • обмен сообщениями в реальном времени

  • список гостей в комнате с их статусами

Перед тем как приступить к разработке вышеперечисленных фич приложения нам потребовалось установить несколько библиотек и настроить их под наши задачи. Ниже вы можете найти ссылки на наши Pull Request-ы и официальные документации:

  • установка и настройка Tailwind - Pull Request, документация

  • интеграция Auth0 - Pull Request, документация

  • настройка SocketIO с NextJS - Pull Request

Перед тем как мы будем объяснять реализацию фич, мы бы хотели показать структуру папок проекта, потому что каждый код-сниппет указывает в каком файле он находится и вам будет легче сориентироваться при дальнейшем чтений.

|- app
|----| index.tsx
|- components
|----| lobby.tsx
|----| control-panel.tsx
|----| chat.tsx
|----| status.tsx
|- contexts
|----| users-connection.tsx
|----| users-settings.tsx
|- hooks
|----| use-is-audio-active.ts
|----| use-media-stream.ts
|----| use-peer.ts
|----| use-screen.ts
|- pages
|----| index.tsx
|----| room
|--------| [roomId].tsx

Переход на страницу-лобби

На главной странице приложения у пользователя есть две возможности: создать новую комнату или присоединиться к существующей. Независимо от выбора действия, пользователь попадает на страницу-лобби, где пользователь может предварительно вкл/выкл своë аудио/видео перед входом в комнату. Логика "страницы-лобби" реализована через useState.

// pages/[roomId].tsx

export default function Room(): NextPage {
  const [isLobby, setIsLobby] = useState(true);
  const { stream } = useMediaStream();
  
  return isLobby
    ? <Lobby stream={stream} onJoinRoom={() => setIsLobby(false)} />
    : <Room stream={stream} />;
}

Так как мы работаем с видео и аудио, а они являются потоком данных в определенном промежутке времени, и чтобы комфортно работать с этим типом нам нужен подходящий интерфейс. К счастью он у нас есть. Протокол MediaCapture и Streams API позволяет создать нам поток (stream) медиа данных. Поток состоит из нескольких треков (tracks), таких как видео и аудио. Наш кастомный хук useMediaStream берëт ответственность за создание и манипуляцию над потоком.

На странице-лобби происходят два действия:

  1. управление первоначальными настройками стрима

  2. вход в комнату.

// components/lobby.tsx
// pseudocode

const Lobby = ({
  stream,
  onJoinRoom,
}: {
  stream: MediaStream;
  onJoinRoom: () => void;
}) => {
  const { toggleAudio, toggleVideo } = useMediaStream(stream);
  
  return (
    <>
        <video srcObject={stream} />
        <button onClick={toggleVideo}>Toggle video</button>
        <button onClick={toggleAudio}>Toggle audio</button>
        <button onClick={onJoinRoom}>Join</button>
    </>
  );
};

Просим заметить, что если track.enabled = true (track - audio / video), то состояние трека выводится из источника к слушателям без посредников (WebSocket, PeerJS), иначе, выводятся пустые кадры. Исходя из этого, не нужно хранить локальный стэйт для того чтобы вкл/выкл аудио/видео.

Полный код реализаций страницы-лобби можете посмотреть здесь.

Переход в комнату

Допустим, что человек на странице-лобби выбрал аудио и видео включенными и затем переходит в комнату. На этом моменте начинается всë самое интересное

Источник: https://habr.com/ru/post/701002/


Интересные статьи

Интересные статьи

RecyclerView - это действительно классный и мощный инструмент для отображения списка(ов) содержимого на Android. Существует масса отличных статей и примеров о различных р...
Год назад во фреймворке Next.js 9.3 появилась поддержка генерирования статических сайтов (Static Site Generation, SSG), что сделало его первым гибридным фреймворком. Я к тому моменту уже ...
Разработка прогнозной модели нейронной сети для нового набора данных может оказаться сложной задачей.Один из подходов состоит в том, чтобы сначала проверить набор данных ...
Предисловие В данной статье я расскажу о конфигурации для вашей сервисов с помощью связки Vault (KV и пока только первой версии, т.е. без версионирования секретов) и Pydantic (Settings...
Встреча двух друзей, живущих в разных городах, всегда приносит много эмоций и заряжает позитивом. Как раз за вдохновением команда топ-менеджеров группы ЛАНИТ и отправилась этой весной в Дублин – ...