На фоне известных событий делать было нечего в рамках профессионального роста пытаюсь освоить WebRTC. Как известно, лучший способ освоения – сделать что-нибудь хотя бы потенциально полезное. А заодно и поделиться-обменяться опытом создания и набивания шишек.

В качестве задачи было решено сделать простое приложение, позволяющее проводить аудиовидеозвонки между двумя (пока что) стационарными или мобильными устройствами в локальной сети без необходимости подключаться к Интернету. Установка и первоначальная настройка такого приложения должны быть простыми настолько, чтобы сколь-нибудь продвинутый эникей без проблем с этим справился и показал пользователям, как делать звонки, а при наличии соответствующих навыков мог бы сделать небольшие доработки в части дизайна и возможностей. Клиентом должно выступать любое устройство, оснащённое устройствами ввода-вывода мультимедиа и позволяющее запустить подходящий браузер (Firefox или Chrome – тестил на майских, кажется, версиях).

Как это сделано


Как известно, технология WebRTC для связи между двумя абонентами предлагает использовать объект типа RTCPeerConnection, а главной задачей разработчика является организация обмена текстовой информацией (SDP-offer, SDP-answer, ICE-кандидат) между вызывающим и вызываемым абонентами. Другими словами, разработчику нужно сначала сделать текстовый чат с API для браузерного JavaScript и далее прикрутить к нему мультимедиа-часть — события RTCPeerConnection и методы передачи и обработки приёма данных.

Выбор технологий для реализации и API текстового чата остаётся за разработчиком. Многие (и в частности Mozilla в своём официальном примере работы RTCPeerConnection) предпочитают использовать WebSocket API и соответствующий сервер – например, на Node.JS. Но, учитывая нашу задачу сделать максимально просто для развёртывания, я решил для начала не переусложнять серверное приложение, тем более для доставки клиентскому устройству страниц и скриптов нужен был дополнительно Web-сервер. Поэтому API решил сделать на xmlHttpRequest с периодическими обращениями клиентов к тому же Web-серверу. Не могу сказать, что это работает идеально с точки зрения расхода ресурсов (и батарей) клиентского устройства и отсутствия тормозов, но работает точно, если при разработке учитывать некоторые нюансы. Возможно, в какой-нибудь следующей версии добавлю сервер WebSocket и переделаю соответствующим образом API, но не всё сразу.

Серверную часть было решено сделать на Lazarus под Windows; сетевые возможности обеспечивает пакет Synapse. В чём-то это, наверное, извращение, и пришлось серьёзно повозиться и набить несколько шишек, чтобы заставить всё работать так, как задумано. Но один exe, две dll (библиотеки OpenSSL), файлы самоподписанного SSL-сертификата и ключа к нему и немножко файлов конфигурации (плюс статика) позволяют сильно не заморачиваться с уровнем техники под сервер и способом запуска приложения. Первую версию этого сервера в 32-битном билде я тестил даже на Asus Eee PC 900 2009 года выпуска под Windows XP, хоть и не обошлось без чита в виде недавней замены штатного супермедленного SSD на более современный и объёмный. Это если говорить за производительность. А «установка» сервера представляет собой распаковку скачанного zip-архива в любую подходящую папку, редактирование JSON-файла конфигурации учётных записей пользователей и запуск exe-файла программы (в окне есть ещё кнопка, но можно указать параметр в командной строке, чтобы Web-сервер запустился сразу). Так или иначе, раздумываю и над более серьёзной серверной частью, благо опыт такой имеется. Всему своё время.

Помимо собственно организации API наш сервер отдаёт статические файлы для браузеров (Web-страницы логина и чата, стили, изображения, скрипты, рингтон). Вообще я старался по возможности обойтись без сторонних библиотек, но из-за того, что с дизайном и html-вёрсткой у меня не ахти, решил всё же воспользоваться jQuery.UI и соответственно jQuery, которые Web-сервер также отдаёт как статику. Все файлы статики лежат в отдельной подпапке папки программы; их, естественно, можно смотреть и даже менять при желании и наличии соответствующих навыков. В JavaScript код прокомментирован, по нему при необходимости можно и учиться.

Как организовать связь


Для организации связи главное – подобрать и объединить в общую сеть клиентские устройства (компьютеры, ноуты, смартфоны, планшеты) и Windows-машинку с «сервером» (она же может выступать и клиентом). Из клиентских устройств я тестил несколько недорогих смартфонов выпуска последних нескольких лет на Android начиная с 7-й версии, а также компьютер и ноут на Windows 10, в т. ч. с двумя подключенными Web-камерами; они показали себя нормально. Первую версию я шутки ради протестил даже на Orange Pi One с Lubuntu (или Kubuntu, не помню уже с ходу) от производителя. На удивление, оно даже работало, хоть видео и тормозило, а страница чата открывалась невесть сколько времени (про загрузку системы и открытие браузера даже говорить не хочется).

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

Работает всё так. Пользователи заходят на «серверную» машинку браузером по протоколу https, используя её IP-адрес или доменное имя. Там они вводят свои логин-пароль и заходят на страницу чата со списком контактов. При щелчке по контакту открывается окно диалога с историей текстовых сообщений (к слову, сервер хранит её только в ОЗУ, в файл пока не умеет), полем для чата и формой аудиовидеозвонка с флажками для выбора аудио и (или) видео. Для совершения видеозвонка пользователь отмечает соответствующие флажки, нажимает кнопку звонка и подтверждает разрешение браузеру. У вызываемого абонента начинает пищать рингтон и открывается форма ответа с такими же флажками. После щелчка по кнопке ответа браузер также спросит разрешение на доступ к мультимедиа-устройствам. Затем открывается окно звонка.

Не могу сказать, что у меня большой опыт работы с ПО для видеоконференций, видеоконсультаций и т. д., но, например, в Google Hangouts на компьютере (как на мобильных устройствах, не знаю) я не увидел возможности включить себялюбимого на полный экран, что в теории может потребоваться на удалённых консультациях, когда тебе нужно хорошо видеть то, что ты показываешь своему собеседнику (например, через заднюю камеру смартфона). В этом чате в диалоге звонка я решил сделать две вкладки с видео – собеседника и самого пользователя. С текущей версии на вкладке пользователя, помимо самого видео, есть поля для выбора камеры и микрофона; с менять их значения можно на лету в ходе беседы. Возможно, это окажется кому-то полезным.

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

Современные особенности работы и реализации WebRTC и вообще работы с мультимедиа на JavaScript


Здесь кратко; подробности можно посмотреть в комментариях в javascript-файле static/js/videoChat.js

  1. Chrome точно, плюс, возможно, и другие браузеры позволяют работать с getUserMedia только на сайтах, доступных по HTTPS
  2. Перечень устройств ввода аудио и видео можно получить только после успешного вызова getUserMedia
  3. Автоматический запуск проигрывания звука средствами JavaScript (через метод play() html-элемента video или audio) возможен только после того, как пользователь покажет активность на сайте – например, щёлкнет по какому-то элементу управления.
  4. Окончание выполнения promise после setLocalDescription у вызывающего абонента нужно придержать хотя бы до того момента, как сервер чата отдаст вызываемому абоненту на обработку отправленный offer. Без этого RTCPeerConnection сразу начнёт отдавать ICE-кандидатов, которых вызываемый абонент до обработки полученного оффера добавить к себе не сможет.
  5. Для переключения устройств ввода «на лету» перед вызовом getUserMedia необходимо остановить все старые треки на RTCPeerConnection. Без этого, что бы пользователь ни выбрал в качестве устройства ввода, оно выбрано не будет.
  6. Во многих описаниях для мобильных устройств говорится про свойство facingMode для выбора фронтальной или задней камер. На самом деле не знаю, как в старых устройствах, но в этом чате на протестированных смартфонах переключение работает и без использования этого свойства. Но строго с учётом п. 5.

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

Шишка разработчика сетевых приложений на Lazarus


Пакет Synapse на текущий момент поддерживает только библиотеки OpenSSL версий 1.0.x; в 1.1 много чего уже реализовано по-другому, другие даже имена библиотек. Кроме того, просто поместить dll-ки в папку с программой недостаточно. Нужен также файл конфигурации (openssl.cnf), путь к которому задаётся через переменную окружения OPENSSL_CONF.

Где скачать


Дистрибутивы программы под Win32 и Win64 и исходники серверной части на Lazarus доступны на странице программы по ссылке www.lubezniy.ru/soft/videochat

P.S.: Кстати, кто-нибудь знает, каким образом можно с помощью Lazarus автоматизировать сборку из одних и тех же исходников двух разных exe — под Win32 и Win64? Кросскомпилятор с Win64 на Win32 есть и работает, но менять каждый раз опции в проекте не то чтобы правильно.

P.P.S.: Кто попробовал, просьба поделиться впечатлениями в комментах.