Содержание
Comet Video API - Вступление
Ссылка на онлайн демо для нетерпеливых
Можно выделить два подхода к организации видео чатов на базе webRTC. Первый – это передача сигнала от каждого участника конференции каждому.
В такой конфигурации нет нужды в центральном сервере и её просто реализовать, но в тех случаях, когда участники находятся за NAT, надо всё равно трафик проксировать через turn сервер. ( Ответ на toster о преодолении NAT )
Но есть ещё проблема в том, что много человек в такую конференцию не добавить, так как с каждым новым участником будет увеличиваться объём трафика. Так как для конференции на 5 человек вам надо будет отправлять свой поток четверым собеседниками. И получать четыре видео потока от них. чтобы видеть всех сразу.
В такой ситуации можно перейти к другому подходу. Когда все абоненты посылают свой сигнал на центральный сервер, а тот в свою очередь посылает каждому абоненту один результирующий видео поток. (Именно так и реализовано Video API в CppComet)
Минусом такой схемы будет необходимость наличия центрального сервера. И ощутимая на него нагрузка.
Но за то это открывает ряд возможностей.
- Больше участников в конференции
- Возможность записи конференции сервером в видео файл
- Меньше нагрузка на сеть у каждого из участников
- Возможности по управлению аудио и видео потоками средствами медиа сервера (можно API запросами к серверу отключать/включать видео и звук у отдельных пользователей, определять кого видят остальные пользователи, контролировать длительность разговора, отключать отдельных абонентов и многое другое)
Video API
В комет сервер встроена интеграция с FreeSwitch упрощающая создание видио конференций и видео чатов. (С возможностью использовать всё это в режиме кластера)
Есть возможность совершать:
- Видео/Аудио звонки (По WebRTC)
- Видео/Аудио конференции на несколько участников (По WebRTC)
- Видео трансляции для стриминга один ко многим (По WebRTC отправка видео от того, кто видео поток публикует и доставка зрителям в форматах hls и mpeg-dash с задержкой от 5 секунд (теоретически минимально возможная задержка для hls потока) до 20 - 40 секунд в зависимости от конфигурации ретрансляторов и настроек качества)
Демо видео конференций
Ещё несколько более длинных gif анимаций
See the Pen CppComet video chat example by Trapenok Victor (@Levhav) on CodePen.
Концепция API видео чатов
- Для работы видео чатов комет сервер использует возможности FreeSwitch просто взаимодействуя с ним по АПИ.
- Комет сервер в состоянии распределять видео и аудио звонки по множеству серверов с FreeSwitch.
- Работа с FreeSwitch полностью скрывается за апи комет сервера.
CometQL API
В CometQL добавлена таблица conference, которая содержит информацию о активных конференциях.
Чтобы создать новую конференцию и добавить в неё участника, надо выполнить запрос
INSERT INTO conference (name, user_id, caller_id, message, profile)VALUES('1', 2, 2, "", "video");
Тут параметры:
- name - имя конференции (состоит только из цифр)
- user_id - идентификатор добавляемого в конференцию пользователя
- caller_id - идентификатор того пользователя, который иницирует звонок (не обязательный параметр)
- message - произвольное сообщение
- profile - режим звонка возможны варианты «audio», «video» (В open source версии и по запросу в поддержку в saas версии можно создавать свои режимы видео конференций, чтобы добавлять водяные знаки, показывать не просто говорящего в данный момет участника конференции, а всех сразу или настраивать правила показа участников конференции на общем холсте. Все возможности перечислены в документации на mod_conference)
Чтобы добавить ещё одного участника, надо выполнить такой же запрос только передать новый user_id и старый name. Соответственно создание конференции из 5 участников потребует 5 запросов вставки в таблицу conference
Пример можно смотреть в файле call.php
Выборка данных из таблицы conference
SELECT * FROM `conference` WHERE name = "101";
Удаление данных из таблицы conference
DELETE FROM `conference` WHERE name = "101" AND user_id = 29130;
Полноценная поддержка оператора `and` в синтаксисе запросов будет реализована позже, так как и без него есть куда более важные задачи и работе отсутствие оператора end не мешает.
Запрос выборки из таблицы `conference` только покажет, кому были отправлены приглашения в конференцию.
SELECT * FROM `conference` WHERE name = "101";
Вовсе не факт, что все эти люди до сих пор в этой конференции находятся.
Чтобы узнать, кто разговаривает в данный момент надо сделать запрос к таблице `conference_members`
SELECT * FROM `conference_members`
В данный момент таблица `conference_members` доступна только для чтения и содержит такие поля
- name - имя конференции
- user_id - идентификатор пользователя
- join_time - количество секунд прошедшее с момента подключения пользователя
- last_talking - количество секунд прошедшее с того момента, когда он что-то говорил в конференции.
JavaScript API
В js надо подключить файл cometVideoApi.js и библиотеку JsSIP v2.0.6
после чего для обработки событий входящего звонка, подключения и отключения надо выполнить следующий код
// Иницируем активацию cometVideoApi cometVideoApi.start({ // Колбек вызываемый перед началом подключения для звонка // Предполагается, что в нём будут заданы настройки для близжайшего звонка // Такие как и параметры audio_remote, video_local, video_remote и возможно ещё какие–то. // А потом будет вызвана функция cometVideoApi.acceptCall(event) // А если не будет вызвана то значит мы не взяли трубку. onCall:function(callEvent) { $(".status").html("Входящий звонок, нажмите ответить."); if(!confirm("Ответить на вызов?")) { // Решили не отвечать на звонок $(".status").html("Решили не отвечать на звонок."); $(".StartCallBtn").show(); return } // Берём трубку, если хотим cometVideoApi.acceptCall({ // Тип звонка 'audio' | 'video' type:'video', // Указываем целевой элемент для видео потока от собеседника video_remote: $("#video_remote")[0], // Указываем целевой элемент для аудио потока от собеседника audio_remote: $("#audio_remote")[0], // Указываем целевой элемент для видео потока от меня (моё собственное изображение из камеры) video_local: $("#video_local")[0], }) $(".status").html("Ожидаем ответа от других участников конференции"); }, /** * Колбек завершения звонка * @param {object} event * { * action:"", // Имя события * status:"", // Статус причины завершения звонка * callInfo:{}, // Информация о звонке * time:1000 // Время длительность звонка * type:"audio" // Тип вызова * } */ onCallEnd:function(event) { $(".root").removeClass("incall") $(".StartCallBtn").show(); $(".status").html("Вызов завершён"); log("onCallEnd", event) }, /** * Колбек поднятия трубки собеседником * @param {object} event * { * action:"accept", // Имя события * callInfo:{}, // Информация о звонке * type:"audio" // Тип вызова который выбран собеседником * } * * Колбек вызывается только один раз для первого ответившего собеседника */ onCallAccept: function(event) { $(".status").html("Получен ответ от другого участника конференции."); log("onAccept", event) }, /** * Колбек, когда я и мой собеседник подключились к серверу * @param {object} event * { * action:"start", // Имя события * callInfo:{}, // Информация о звонке * type:"audio" // Тип вызова * } */ onCallStart: function(event) { $(".status").html("Подключение к медиасерверу выполнено. Разговор начался."); $(".root").addClass("incall") log("onCallStart", event) }, onOut: function(event) { // Выход участника из конференции $(".status").html("Один из собеседников покинул конференцию"); log("onOut", event) }, onIn: function(event) { // Вход участника в конференцию $(".status").html("К конференции присоединился ещё один человек."); log("onIn", event) }, })
Настройка бэкенда для видео звонков
Настройка comet.ini
Секция [freeswitch] в comet.ini поднимает на указанном порту и ip адресе http интерфейс который отвечает за механизм единой с freeswitch авторизации пользователей.
[freeswitch] ip = 0.0.0.0 thread_num = 3 statistics = 10 port = 84
Секция [sip] содержит список адресов и портов серверов freeswitch которые готовы обрабатывать видео и аудио звонки.
[sip] port = 7443 host = ecort-n1.comet.su ; pipesalt = pipeSecretSalt
Настройка FreeSwitch
Есть docker образ с нужными настройками для работы FreeSwitch с комет сервером.
Скачать образ
docker pull cppcomet/freeswitch-video
Запустить
docker run -v /root/FreeSwitch-in-docker/conf:/usr/local/freeswitch/conf -v /root/FreeSwitch-in-docker/certs:/usr/local/freeswitch/certs --net host -it cppcomet/freeswitch-video
Нужно указать доступ к ssl сертификату и папке с файлами конфигурации.
Сборка FreeSwitch из исходников с нужными модулями
Нужно собрать FreeSwitch по инструкции FreeSwitch Building from source
Затем включить в сборку и в автозагрузку следующие модули
- mod_rtmp - позволит вести трансляции видео звонков и конференций
- mod_xml_curl - позволит FreeSwitch серверу использовать те же данные авторизации что имеются в комет сервере в таблице users_auth
- mod_av
- mod_sndfile
- mod_shell_stream
- mod_ilbc
- mod_h26x
- mod_siren
- mod_isac
Итоговый скрипт для сборки с нужными модулями
wget -O - https://files.freeswitch.org/repo/deb/debian/freeswitch_archive_g0.pub | apt-key add - echo "deb http://files.freeswitch.org/repo/deb/freeswitch-1.6/ jessie main" > /etc/apt/sources.list.d/freeswitch.list apt-get update apt-get install -y --force-yes freeswitch-video-deps-most # because we're in a branch that will go through many rebases it's # better to set this one, or you'll get CONFLICTS when pulling (update) git config --global pull.rebase true # then let's get the source. Use the -b flag to get a specific branch cd /usr/src/ git clone https://freeswitch.org/stash/scm/fs/freeswitch.git -bv1.6 freeswitch cd freeswitch ./bootstrap.sh -j sed -e "s/#codecs\/mod_isac/codecs\/mod_isac/g" /usr/src/freeswitch/modules.conf | sed -e "s/#applications\/mod_av/applications\/mod_av/g" | sed -e "s/#endpoints\/mod_rtmp/endpoints\/mod_rtmp/g" | sed -e "s/#xml_int\/mod_xml_curl/xml_int\/mod_xml_curl/g" | sed -e "s/#applications\/mod_av/applications\/mod_av/g"| sed -e "s/#formats\/mod_sndfile/formats\/mod_sndfile/g" | sed -e "s/#formats\/mod_shell_stream/formats\/mod_shell_stream/g" | sed -e "s/#codecs\/mod_ilbc/codecs\/mod_ilbc/g"| sed -e "s/#codecs\/mod_h26x/codecs\/mod_h26x/g" | sed -e "s/#codecs\/mod_siren/codecs\/mod_siren/g"| sed -e "s/#codecs\/mod_isac/codecs\/mod_isac/g" > /usr/src/freeswitch/modules.tmp cat /usr/src/freeswitch/modules.tmp > /usr/src/freeswitch/modules.conf rm -rf /usr/src/freeswitch/modules.tmp ./configure make make install # create user 'freeswitch' # add it to group 'freeswitch' # change owner and group of the freeswitch installation cd /usr/local groupadd freeswitch adduser --quiet --system --home /usr/local/freeswitch --gecos "FreeSWITCH open source softswitch" --ingroup freeswitch freeswitch --disabled-password chown -R freeswitch:freeswitch /usr/local/freeswitch/ chmod -R ug=rwX,o= /usr/local/freeswitch/ chmod -R u=rwx,g=rx /usr/local/freeswitch/bin/* sed -e "s/EnvironmentFile=-\/etc\/default\/freeswitch/EnvironmentFile=-\/etc\/freeswitch/g" /usr/src/freeswitch/debian/freeswitch-systemd.freeswitch.service | sed -e "s/PIDFile=\/run\/freeswitch\/freeswitch.pid/PIDFile=\/usr\/local\/freeswitch\/run\/freeswitch.pid/g" > /etc/systemd/system/freeswitch.service cp /usr/local/freeswitch/bin/freeswitch /usr/bin/freeswitch mkdir /run/freeswitch systemctl daemon-reload systemctl enable freeswitch
Потом надо выполнить инструкции Debian+Post-Install+Tasks
Включение модулей в автозагрузку
Для включения в автозагрузку модулей надо убедится, что в файле /usr/local/freeswitch/conf/autoload_configs/modules.conf.xml есть следующие строки
<load module="mod_av"/> <load module="mod_sndfile"/> <load module="mod_shell_stream"/> <load module="mod_rtmp"/> <load module="mod_xml_curl"/> <load module="mod_ilbc"/> <load module="mod_h26x"/> <load module="mod_siren"/> <load module="mod_isac"/>
Настройка xml_curl.conf на freeswitch
В файле
/usr/local/freeswitch/conf/autoload_configs/xml_curl.conf.xml
надо вписать порт и адрес для подключения к комет серверу с реквизитами, которые мы задали в секции [freeswitch]
<configuration name="xml_curl.conf" description="cURL XML Gateway"> <bindings> <binding name="directory"> <param name="gateway-url" value="http://app.comet-server.ru:84/directory" bindings="directory"/> <param name="method" value="GET" /> </binding> </bindings> </configuration>
Эта настройка отвечает за то, чтобы пользователи, которые авторизованы на комет сервере могли по тем же данным авторизации подключится к freeswitch для совершения звонка.
Подробности по работе модуля тут mod_xml_curl
Настройка conference.conf.xml на freeswitch
Для настройки разных профилей работы видео конференций надо отредактировать файл /usr/local/freeswitch/conf/autoload_configs/conference.conf.xml
Документация на mod_conference настройки по умолчанию годятся.
Если хотите транслировать конференцию зрителям по rtmp то в настройки профиля конференции можно вписать команду:
<param name="auto-record" value="rtmp://comet.su/cam1/${conference_name}"/>
С указанием вашего nginx c nginx-rtmp-module
Настройка dialplan/default.xml на freeswitch
Файл /usr/local/freeswitch/conf/dialplan/default.xml содержит планы набора номеров. Он используется для определения что надо делать при звонке на конкретные номера.
В секцию
<context name="default">
надо добавить extension для набора видео конференции
<extension name="comet_conferences"> <condition field="destination_number" expression="^([0-9]{1,9}\*[0-9A-z\-_]+)\*([0-9A-z\-_]+)$"> <!-- Если у вас только аудио звонок то можно добавить эту строку <action application="set" data="absolute_codec_string=PCMU,GSM"/> --> <action application="answer"/> <action application="conference" data="$1-${domain_name}@$2"/> </condition> </extension>
Настройка vars.xml на freeswitch
Полезно в vars.xml задать стойкий пароль в строке, он будет использоваться комет сервером для api вызовов.
<X-PRE-PROCESS cmd="set" data="default_password=1234"/>
И надо установить переменную domain=FSdefaultDomain
<X-PRE-PROCESS cmd="set" data="domain=FSdefaultDomain"/>
Настройка event_socket.conf на freeswitch
В event_socket.conf настраиваем на каком порту и с каким паролем freeswitch будет позволять подключиться для выполнения api команд.
<configuration name="event_socket.conf" description="Socket Client"> <settings> <param name="nat-map" value="false"/> <param name="listen-ip" value="0.0.0.0"/> <param name="listen-port" value="8021"/> <param name="password" value="MyPassword"/> <param name="apply-inbound-acl" value="0.0.0.0/32"/> </settings> </configuration>
Настройка https на freeswitch
Обычно после установки freeswitch в папке
/usr/local/freeswitch/certs/wss.pem
лежит само-подписанный сертификат. Для работы видео и аудио чатов их надо заменить на валидные так как иначе с https сайта не получится подключится к freeswitch по websockets для обмена sip командами с freeswitch
Для получения сертификатов удобно использовать Let's Encrypt. Я использую эту инструкцию
А в качестве post-hook для обновления сертификата такую команду:
cat /etc/letsencrypt/live/fs-n3.elevenow.com/fullchain.pem > /usr/local/freeswitch/certs/wss.pem && cat /etc/letsencrypt/live/fs-n3.elevenow.com/privkey.pem >> /usr/local/freeswitch/certs/wss.pem && cat /etc/letsencrypt/live/fs-n3.elevenow.com/fullchain.pem > /usr/local/freeswitch/certs/dtls-srtp.pem && cat /etc/letsencrypt/live/fs-n3.elevenow.com/privkey.pem >> /usr/local/freeswitch/certs/dtls-srtp.pem && systemctl stop freeswitch && rm -rf /var/lib/freeswitch/db && systemctl start freeswitch
Настройка трансляций в nginx
Для трансляций надо собрать nginx с модулем nginx-rtmp-module вот неплохая инструкция
Вот пример конфигурации nginx
worker_processes 1; rtmp { server { live on; listen 1935; chunk_size 512; buflen 1s; idle_streams off; application cam1 { live on; record off; hls on; hls_path /tmp/hls; hls_fragment 2s; hls_playlist_length 20s; hls_type live; #hls_continuous on; hls_fragment_naming sequential; hls_nested on; dash on; dash_path /tmp/dash; dash_fragment 2s; dash_playlist_length 10s; dash_nested on; } } } server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; index index.html index.htm index.nginx-debian.html; server_name _; include acme; location /dash { root /tmp; add_header Access-Control-Allow-Origin *; add_header Cache-Control no-cache; } location / { add_header Cache-Control no-cache; add_header Access-Control-Allow-Origin *; root /tmp; } } server { listen 0.0.0.0:443; server_name comet-server.com; ssl on; ssl_certificate /etc/letsencrypt/live/stream-n1.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/stream-n1.example.com/privkey.pem; ssl_session_timeout 70m; keepalive_disable none; lingering_close always; send_timeout 3600s; client_max_body_size 100m; include acme; location /dash { root /tmp; add_header Access-Control-Allow-Origin *; add_header Cache-Control no-cache; } location / { add_header Cache-Control no-cache; add_header Access-Control-Allow-Origin *; root /tmp; } }
Полезными могут быть статьи
Обсуждение