Ссылка на онлайн демо для нетерпеливых
Можно выделить два подхода к организации видео чатов на базе webRTC. Первый – это передача сигнала от каждого участника конференции каждому.
В такой конфигурации нет нужды в центральном сервере и её просто реализовать, но в тех случаях, когда участники находятся за NAT, надо всё равно трафик проксировать через turn сервер. ( Ответ на toster о преодолении NAT )
Но есть ещё проблема в том, что много человек в такую конференцию не добавить, так как с каждым новым участником будет увеличиваться объём трафика. Так как для конференции на 5 человек вам надо будет отправлять свой поток четверым собеседниками. И получать четыре видео потока от них. чтобы видеть всех сразу.
В такой ситуации можно перейти к другому подходу. Когда все абоненты посылают свой сигнал на центральный сервер, а тот в свою очередь посылает каждому абоненту один результирующий видео поток. (Именно так и реализовано Video API в CppComet)
Минусом такой схемы будет необходимость наличия центрального сервера. И ощутимая на него нагрузка.
Но за то это открывает ряд возможностей.
В комет сервер встроена интеграция с FreeSwitch упрощающая создание видио конференций и видео чатов. (С возможностью использовать всё это в режиме кластера)
Есть возможность совершать:
Ещё несколько более длинных gif анимаций
See the Pen CppComet video chat example by Trapenok Victor (@Levhav) on CodePen.
В CometQL добавлена таблица conference, которая содержит информацию о активных конференциях.
Чтобы создать новую конференцию и добавить в неё участника, надо выполнить запрос
INSERT INTO conference (name, user_id, caller_id, message, profile)VALUES('1', 2, 2, "", "video");
Тут параметры:
Чтобы добавить ещё одного участника, надо выполнить такой же запрос только передать новый 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` доступна только для чтения и содержит такие поля
В 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) }, })
Секция [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
Есть 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 Building from source
Затем включить в сборку и в автозагрузку следующие модули
Итоговый скрипт для сборки с нужными модулями
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"/>
В файле
/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
Для настройки разных профилей работы видео конференций надо отредактировать файл /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
Файл /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 задать стойкий пароль в строке, он будет использоваться комет сервером для api вызовов.
<X-PRE-PROCESS cmd="set" data="default_password=1234"/>
И надо установить переменную domain=FSdefaultDomain
<X-PRE-PROCESS cmd="set" data="domain=FSdefaultDomain"/>
В 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>
Обычно после установки 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-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; } }
Полезными могут быть статьи