На фоне известных событий делать было нечего в рамках профессионального роста пытаюсь освоить 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 на компьютере (как на мобильных устройствах, не знаю) я не увидел возможности включить себялюбимого на полный экран, что в теории может потребоваться на удалённых консультациях, когда тебе нужно хорошо видеть то, что ты показываешь своему собеседнику (например, через заднюю камеру смартфона). В этом чате в диалоге звонка я решил сделать две вкладки с видео – собеседника и самого пользователя. С текущей версии на вкладке пользователя, помимо самого видео, есть поля для выбора камеры и микрофона; с менять их значения можно на лету в ходе беседы. Возможно, это окажется кому-то полезным.
Теперь вкратце опишу набитые при разработке шишки; возможно, это поможет кому-то при разработке и отладке своих решений.
Здесь кратко; подробности можно посмотреть в комментариях в javascript-файле static/js/videoChat.js
Перечень, скорее всего, не исчерпывающий. Полагаю, в ходе дальнейшего развития много чего ещё всплывёт. Если кто-то знает, как обойти ограничения и соответственно упростить программу или работу с ней, просьба писать в комментариях.
Пакет 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.: Кто попробовал, просьба поделиться впечатлениями в комментах.
В качестве задачи было решено сделать простое приложение, позволяющее проводить аудиовидеозвонки между двумя (пока что) стационарными или мобильными устройствами в локальной сети без необходимости подключаться к Интернету. Установка и первоначальная настройка такого приложения должны быть простыми настолько, чтобы сколь-нибудь продвинутый эникей без проблем с этим справился и показал пользователям, как делать звонки, а при наличии соответствующих навыков мог бы сделать небольшие доработки в части дизайна и возможностей. Клиентом должно выступать любое устройство, оснащённое устройствами ввода-вывода мультимедиа и позволяющее запустить подходящий браузер (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
- Chrome точно, плюс, возможно, и другие браузеры позволяют работать с getUserMedia только на сайтах, доступных по HTTPS
- Перечень устройств ввода аудио и видео можно получить только после успешного вызова getUserMedia
- Автоматический запуск проигрывания звука средствами JavaScript (через метод play() html-элемента video или audio) возможен только после того, как пользователь покажет активность на сайте – например, щёлкнет по какому-то элементу управления.
- Окончание выполнения promise после setLocalDescription у вызывающего абонента нужно придержать хотя бы до того момента, как сервер чата отдаст вызываемому абоненту на обработку отправленный offer. Без этого RTCPeerConnection сразу начнёт отдавать ICE-кандидатов, которых вызываемый абонент до обработки полученного оффера добавить к себе не сможет.
- Для переключения устройств ввода «на лету» перед вызовом getUserMedia необходимо остановить все старые треки на RTCPeerConnection. Без этого, что бы пользователь ни выбрал в качестве устройства ввода, оно выбрано не будет.
- Во многих описаниях для мобильных устройств говорится про свойство 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.: Кто попробовал, просьба поделиться впечатлениями в комментах.
Cable
Может попробовать реализовать через какой-нибудь Electron? Будет кросс-платформенно
lubezniy Автор
Что реализовать? На клиентской стороне работа через браузер сама по себе даёт кроссплатформенность. На серверной — Lazarus тоже среда кроссплатформенная и при этом даёт компактные и работающие даже на малых ресурсах исполняемые файлы, хотя под Linux я думаю сделать что-то посерьёзнее, чем десктопную форму.
Cable
Ну я про десктопную форму и говорю. Насколько я понял, сейчас под винду только. Electron поможет сделать кроссплатформенную десктопную форму и на винду и на unix подобные системы одной сборкой)
lubezniy Автор
Lazarus (по сути Free Pascal) в Linux тоже умеет. Насчёт Electron есть смысл думать, если он кучу зависимостей за собой не понянет или не влинкует в итоговый файл. Сейчас exe-шник, собранный под x64, занимает меньше 3 МБайт, к нему ещё 2,5 МБайт dll-ок OpenSSL. А сколько получится у Electron?..
Cable
Возьмем как пример Discord — написан на ReactJS + Electron + еще что-то. Вроде никаких проблем при использовании у людей не возникало, даже если инсталлятор весит больше 50мб)
lubezniy Автор
Discord же через Интернет работает, нет? А у нас видеосвязь для локальной сети, что может подразумевать не лучшее качество имеющегося подключения к Интернету и соответственно проблемы со скачиванием больших файлов. Ну и техника тоже всякая попадается. В этом плане я предпочитаю, насколько это возможно, сделать так, чтобы ПО работало даже на старой технике, которая до поры до времени могла вообще валяться в пыльном загашнике; меньше будет проблем у пользователей и соответственно жалоб на тех, кто решил таки скачать программу и её использовать.
Cable
Куда засервится, там и будет. Дискорд — да, смотрит на внешние сервера, а это и на локалке можно настроить) Старый комп с 2гб оперативы и 512мб видяхи думаю вытянет приложение) Хотя я не знаю на сколько старый комп можно протестить?)
lubezniy Автор
Ну, Asus EEE PC 900 — штука ещё похлеще. Но, тем не менее, встречается. В конце концов на той же виртуальной машине с одним ядром, гигом ОЗУ и без доступной видюхи вполне можно сделать и запустить рабочую и более шуструю реализацию того же API. Спрашивается: зачем тратить лишние ресурсы, если есть возможность обойтись без этого, не увеличивая существенно объём возни с организацией процесса? Лучше эти ресурсы пользователю оставить, ему пригодятся.
lubezniy Автор
И, кстати, прочитал сегодня новость о выходе новой ReactOS. Попробую на ней тоже сервер потестить; сомневаюсь, правда, что с ходу заработает (если вообще заработает), но шансы явно выше, чем у ПО на Electron.
Cable
А с OpenSSL можно работать через npm пакет на чистом JS)
lubezniy Автор
Ну так какая разница, npm-пакет или Synapse? Всё равно что то, что другое — дополнительная либа.