Реализуем трехсторонний видеочат на следующих платформах: Google Chrome на десктопе, Android приложение на планшете, и iOS приложение для Apple iPhone.

Напомним два основных принципа построения видеочата:

  1. Каждый подключившийся пользователь может отправить (опубликовать) свой видеопоток на сервер.
  2. Пользователи знают имена видеопотоков друг-друга и имеют возможность их воспроизвести.

Таким образом, в видеочате из трех участников, каждому из участников придется воспроизводить по два видеопотока.

videochat-ios-android-browser

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

Публикуют потоки

1. Alice: session.createStream({name:»stream_alice»}).publish();
2. Boris: session.createStream({name:»stream_boris»}).publish();
3. Anna: session.createStream({name:»stream_anna»}).publish();

Играют потоки

4. Alice: session.createStream({name:»stream_anna»}).play();
5. Alice: session.createStream({name:»stream_boris»}).play();
6. Boris: session.createStream({name:»stream_alice»}).play();
7. Boris: session.createStream({name:»stream_anna»}).play();
8. Anna: session.createStream({name:»stream_alice»}).play();
9. Anna: session.createStream({name:»stream_boris»}).play();

От разработчика в данном случае требуется организовать передачу названий и статусов видеопотоков участникам видеочата. Это можно сделать на любой подходящей технологии, например PHP, websockets, Node.js и т.д., так как передача имени видеопотока ничем не отличается от передачи обычного текстового сообщения от одного пользователя к другому.

У потока может быть три основных статуса: PUBLISHING, PLAYING, STOPPED Для успешного воспроизведения, запрашиваемый поток должен быть в активном статусе PUBLISHING.

active-state-PUBLISHING

На этой схеме показано, как именно можно реализовать обмен именами и статусами потоков, в упрощенном варианте, когда Алиса демонстрирует свой видеопоток Борису и Анне. Такая процедура занимает 8 шагов, и может быть названа сигналингом, т.к. В результате этой процедуры проводится согласование:

  1. Алиса отправляет видеопоток на WCS-сервер.
  2. Алиса получает от сервера подтверждение в виде статуса PUBLISHING
  3. Алиса отправляет Борису сообщение, о том, что ее поток готов к воспроизведению.
  4. Алиса отправляет Анне сообщение, о том, что ее поток готов к воспроизведению.
  5. Анна воспроизводит видеопоток с WCS-сервера.
  6. Анна получает подтверждение статус PLAYING.
  7. Борис воспроизводит видеопоток с WCS-сервера.
  8. Борис получает подтверждение статус PLAYING.

В результате может быть реализован произвольный сценарий взаимодействия: с подключением двух и более пользователей к чату, с подключением просто зрителей, и т.д.

Комнаты

Организовать обмен именами видеопотоков и их статусами не так уж и сложно технически, но это требует определенных трудозатрат и работы с кодом.

С другой стороны, для такой задачи напрашивается какое-то универсальное решение, которое могло бы помочь пользователям быстро согласовать потоки и попасть в чат. Такое решение называется Комнаты или Room API.

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

Таким образом, имеем четыре объекта, которые полностью покрывают работу с комнатами:

  • Room — комната
  • Stream — видеопоток
  • Participant — участник
  • Message — сообщение

functionality-of-room

Room API дает возможность кроссплатформенно использовать перечисленные выше абстракции: Room, Stream, Participant и Message для реализации следующих функций:

Подключение

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

Стриминг

  • Пользователь может опубликовать видеопоток внутри комнаты.
  • Пользователь получает уведомления о статусах видеопотоков других участников.
  • Пользователь может воспроизвести видеопоток внутри комнаты.

Сообщения

  • Пользователи могут обмениваться сообщениями внутри комнаты
  • Пользователи могут обмениваться изображениями или другим контентом, если он упакован в текстовый формат
  • Сообщение можно отправить одному или нескольким участникам

Room API

Для Web платформы, комнаты были реализованы в виде JavaScript — модуля со следующими основными функциями:

1. Получаем соединение с сервером.

var connection = Flashphoner.roomApi.connect({urlServer: "wss://host:8443", username: "Alice"});

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

connection.join({name: "room1"});

3. Получаем список участников комнаты.

var participants = room.getParticipants();

4. Отправляем свой видеопоток в комнату.

room.publish({
	display: document.getElementById("localDisplay"),
        constraints: constraints,
        record: false,
        receiveVideo: false,
        receiveAudio: false
});

5. Воспроизводим поток участника.

participant.getStreams()[0].play(document.getElementById(pDisplay))

6. Следим за участниками комнаты:

connection.on(ROOM_EVENT.JOINED, function(participant){...});
connection.on(ROOM_EVENT.LEFT, function(participant){...});
connection.on(ROOM_EVENT.PUBLISHED, function(participant){...});

JOINED — к комнате присоединился новый участник
LEFT — участник вышел
PUBLISHED — участник опубликовал свой видеопоток

7. Получаем сообщения от других участников.

connection.on(ROOM_EVENT.MESSAGE, function(participant){...});

8. Отправляем приватное сообщение конкретному участнику

participants[i].sendMessage(message);

9. Отправляем сообщение всем участникам сразу.

var participants = room.getParticipants();
        for (var i = 0; i < participants.length; i++) {
            participants[i].sendMessage(message);
        }
}

Таким образом, реализация комнат дает простой обмен сообщениями и статусами всех подключенных к комнате участников.

Ограничения Room API

Вся логика при работе с комнатами ложится на клиента. Сервер управляет только только базовым функционалом комнаты:

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

Таким образом, аутентификация, права доступа, роли участников (модератор, зритель, ведущий), и другая логика, должны быть реализованы на клиенте и / или стороннем бэк-энде. Комнаты лишь помогают произвести быстрый обмен информацией о видеопотоках и статусах.

Комнаты для Web, Android, iOS

В каждой из SDK (Web, Android, iOS) для работы с сервером, есть API для работы с комнатами.

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

Web SDK

connection.join({name: "room1"});

Android SDK

Room room = roomManager.join(roomOptions);

iOS SDK

room = [roomManager join:options];

Таким образом в одну и ту же комнату может войти три участника с трех разных платформ:

  • Web — браузер
  • Мобильное приложение для Android
  • Мобильное приложение для iOS

Тестовые приложения для работы с комнатами

Ниже мы приведем три тестовых приложения для работы с комнатами. Каждое из них имеет открытый код и может быть собрано из исходников. Каждое из следующих приложений позволяет обмениваться видеопотоками и сообщениями трём участникам:

  • Conference for Web
  • Conference for Android
  • Conference for iOS

Conference for Web

Код этого приложения доступен для скачивания здесь. В HTML-коде можно найти два div-элемента

Эти элементы отвечают за отображение видео участников. div — элемент localDisplay отвечает за отображение видео, захваченного с камеры.

С помощью кнопок Join / Leave можно войти и выйти из комнаты соответственно. С помощью Stop / Start можно отправить видео в комнату или остановить вещание. Поле Login должно быть уникальным, т.к. идентифицирует участника.

Conference-for-Web

Следующий элемент интерфейса — это текстовый чат. Этот чат отображает сообщения полученные или отправленные другими пользователями, а также отображает информацию о событиях, происходящих в комнате.

text-chat

И последний элемент — это ссылка на приглашение. Если пользователь вошел первым, создается новая комната. В данном случае с именем roomName=room-fb41b7. Если второй пользователь укажет этот же roomName, он попадет в ту же комнату. В приложении Conference for Web приглашения реализованы генерацией ссылки входа, содержащей roomName в качестве параметра. В версиях приложения для Android и iOS, имя комнаты указывается напрямую в интерфейсе.

Conference-for-Web-Android-iOS

Таким образом, тестовое приложение Conference for Web реализует все заложенные выше функции комнат и позволяет нескольким пользователям подключиться к одной и той же комнате и обмениваться видеопотоками и текстовыми сообщениями. Ниже на скриншоте показано как работает комната с тремя участниками. Было открыто три вкладки браузера Google Chrome и на каждой из вкладок было инициировано подключение к комнате.

initiated-connection-to-the-room

Conference for Android

Код этого приложения доступен для скачивания здесь. В приложении можно ввести имя комнаты прямо из UI. В остальном приложение очень похоже на Conference for Web, которое было описано выше и имеет те же элементы интерфейса:

  • Кнопки Leave / Join для входа и выхода из комнаты.
  • Два видео окна для воспроизведения видео участников.
  • Одно видео окно для отображения захвата видео с камеры.
  • Текстовый чат с системной информацией и сообщениями участников

Conference-for-Android

Conference for iOS

Код приложения доступен для скачивания здесь. Интерфейс приложения для iOS уже практически ничем не отличается от приложения под Android, с точностью до названия кнопок и контролов.

В итоге мы сделали тест и собрали все три платформы в одной комнате с номером 3119d6.

Браузер Google Chrome — это заяц, который выбирается из норы:

conference-for-iOS-Google-Chrome

Android 5.1.1 на планшете Asus — это цветочный горшок.

conference-for-iOS-Android 5.1.1

iOS 10.1.1 на iPhone 6 — это Фикус Бенджамина на подоконнике.

conference-for-iOS-iOS 10.1.1

Ниже показан полный скриншот с iOS — устройства:

conference-for-iOS-device

Таким образом, мы завершили тестирование и обзор всех трех приложений, построенных на основе Room API, и можем переходить к исходному коду и сборке.

conference-for-iOS-Room API

Сборка приложения Conference for iOS из исходников

Покажем как собрать пример Conference for iOS и опишем основные куски кода примера. Первым делом нужно обзавестись Mac-железом и установить последний Xcode.

Процедура сборки требует установки Cocoapods, скачивания кода примеров и SDK для iOS.

Собирать будем в терминале, а потом откроем проект в Xcode.

1. Устанавливаем Cocoapods

sudo gem install cocoapods

2. Скачиваем все примеры с github

git clone https://github.com/flashphoner/wcs-ios-sdk-samples.git

3. Скачиваем iOS SDK

wget https://flashphoner.com/downloads/builds/flashphoner_client/wcs-ios-sdk/WCS-iOS-SDK-2.3.0.tar.gz

4. Распаковываем архив

tar -xvzf WCS-iOS-SDK-2.3.0.tar.gz

building-Conference-for-iOS-application

5. Копируем папку FPWCSApi2.framework в примеры

cp -R FPWCSApi2.framework wcs-ios-sdk-samples

framework

6. Запускаем сборку.

./build_example.sh

Если сборка прошла успешно, в терминале выводится **ARCHIVE SUCCEDED**

iOS-application-from-source-codes

После того как примеры собраны из консоли с помощью Cocoapods, подтянуты и сконфигурированы все зависимости и дальнейшие сборки примеров можно проводить непосредственно из Xcode.

7. Открываем WCSExample.xcworkspace в Xcode

сonference-for-iOS-application

8. Выбираем Generic iOS Device в целях сборки. Запускаем сборку примера Conference из меню Product / Build и дожидаемся завершения.

conference-from-source-code

9. Подключаем iPhone по USB и запускаем собранное приложение Conference. В консоли появляется отладочная информация.

Building-Conference

10. На экране iPhone появляется приложение Conference

Conference-application

Таким образом, мы собрали мобильное приложение Conference для iOS из исходников и использовали iOS SDK версии 2.3.0 + Cocoapods для этой сборки. В результате мы смогли запустить это приложение на iPhone 6, подключенном по USB к Mac, на котором производилась эта сборка.

Код для Web, Android, iOS видеочатов

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

Web Android iOS
Код JavaScript Java Objective-C
Основной код conference.js ConferenceActivity.java ViewController.m
Подключение к серверу connection = Flashphoner.roomApi.connect({urlServer: url, username: username}); roomManager = Flashphoner.createRoomManager(roomManagerOptions); roomManager = [FPWCSApi2 createRoomManager:options error:&error];
Подключение к комнате connection.join({name: getRoomName()}); room = roomManager.join(roomOptions); room = [roomManager join:options];
Получение списка участников var participants = room.getParticipants(); room.getParticipants() NSDictionary *participants = [room getParticipants];
Отправка видеопотока в комнату room.publish({ display: document.getElementById(«localDisplay»), constraints: constraints, record: false, receiveVideo: false, receiveAudio: false }); stream = room.publish(localRenderer); publishStream = [room publish:_localDisplay];
Воспроизводим видеопоток участника participant.getStreams()[0].play(document.getElementById(pDisplay)); participant.play(participantView.surfaceViewRenderer); [participant play:pv.display];
Отслеживаем присоединение новых участников к комнате connection.on(ROOM_EVENT.JOINED, function(participant){…}); public void onJoined(final Participant participant){…}; [room on:kFPWCSRoomParticipantEventJoined participantCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomParticipant *participant){…}]
Отслеживаем покидание участниками комнаты connection.on(ROOM_EVENT.LEFT, function(participant){…}); public void onLeft(final Participant participant){…} [room on:kFPWCSRoomParticipantEventLeft participantCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomParticipant *participant){…}]
Отслеживаем публикацию видеопотока участником комнаты connection.on(ROOM_EVENT.PUBLISHED, function(participant){…}); public void onPublished(final Participant participant){…} [room on:kFPWCSRoomParticipantEventPublished participantCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomParticipant *participant){…}]
Получаем входящее сообщение от других участников connection.on(ROOM_EVENT.MESSAGE, function(participant){…}); public void onMessage(final Message message){…} [room onMessageCallback:^(FPWCSApi2Room *room, FPWCSApi2RoomMessage *message){…}]
Отправляем сообщение одному из участников participants[i].sendMessage(message); participant.sendMessage(text); [participant sendMessage:_messageBody.text];

Итого получаем по 10 основных конструкций на каждую из платформ. Каждую из перечисленных конструкций вы сможете найти в соответствующем файле с кодом. Все три приложения Web, iOS и Android были протестированы с последней сборкой Web Call Server 5 — сервера для видеочатов и low-latency трансляций с поддержкой технологии WebRTC.

Ссылки

Web Call Server — сервер для WebRTC видеочатов
EC2 launch — запуск готового образа на Amazon EC2
Install — установка сервера на VPS или Dedicated хост
Cocoapods — менеджер зависимостей для сборки

Web SDK Android SDK iOS SDK
SDK html html html
Скачать SDK tar.gz aar tar.gz
Пример приложения чата с комнатами WCS demo Google Play Только Ad hoc
Основной файл кода примера Conference.js ConferenceActivity.java ViewController.m
Исходный код всех примеров github github github
Описание процедуры сборки примеров из исходного кода html html html
Описание кода примера с комнатами Conference html html

Статьи по теме

Разрабатываем видеочат между браузером и мобильным приложением

Особенности публикации видеочат — iOS приложения в App Store

Возможности продукта

Видеочат в браузере

Видеоконференции WebRTC