Я работаю с MFF и GH. Дружат ли другие браузеры с WebRTC, можно узнать, зайдя на sipjs.com — там без регистрации можно полюбоваться на себя в двух экземплярах (если есть веб-камера), послать себе сообщение или файл. И все это на одной странице. Неинтересно. Интересно, когда я на одной странице, а мой визави на другой. Демо-пример нужно чуть-чуть подправить...
Как это все работает (в моем понимании):
WebRTC — это соединение двух UserAgent (т.е. двух браузеров, далее UA) точка-точка, при котором сигнал с веб-камеры, захваченной одним браузером, стремительным потоком передается другому браузеру.
Чтобы установить соединение и прорваться через частокол NAT'ов нужен SIP и STUN сервер. Оба UA должны быть зарегистрированы на SIP сервере. Что-то типа "1234@myfreefreefreeswitch.ru".
UA с именем "1234@..." и паролем "111" авторизуется на SIP-сервере "myfreefreefreeswitch.ru" и говорит: хочу связаться с UA "5678@..." для передачи голоса. Если оба в сети, сервер их соединяет.
Браузер спрашивает пользователя: "Микрофон просят. Дадим?". Дадим, и получим voice-ip.
Проверить видео-чат с двух разных компьютеров можно на основе демо-примера с sipjs.com, используя в качестве SIP/STUN-сервера sipjs.onsip.com.
Sipjs.onsip.com не требует предварительной регистрации. Он обслуживает пары UA с именами
"alice.случайная строка@sipjs.onsip.com" и
"bob.та же самая случайная строка@sipjs.onsip.com".
На самом деле "bob." или "alice." не обязательны. Можно подключиться с любым уникальным именем. Но это неприлично.
Случайные строки в их демо-примере генеряться JS при открытии страницы и зачем-то передаются на сервер в document.cookie. К SIP-протоколу куки отношения не имеют, видимо, нужны для чего-то другого (например, чтобы зарегистрировать нового любопытного пользователя).
Для проверки видео-чата с 2-х компьютеров http-сервер не обязателен, достаточно 2-х html страниц (на одной: "я Боб, хочу связаться с Алисой", на другой: "я Алиса, хочу связаться с Бобом") и одного скрипта.
Страницы почти одинаковые (сделаны по одному шаблону).!!! Прежде, чем их открыть надо в обеих страницах одинаково исправить 1 строку !!!
ЭТО ВАЖНО!!! var token = '42c3';
42c3 надо исправить на любую длинную строку (англ.буквы и цифры), иначе ваши Боб и Алиса войдут в конфликт с теми, кто не исправил.
Кроме того, возможно, что ваша строка в какой-то момент устареет и все перестанет работать. Замените ее в обеих страницах на новую одинаковую строку.
Помните: это демо-пример от sipjs.com и onsip.com используется не совсем так, как они планировали. Надеюсь, они не обидятся — мы же их популяризируем.
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>Alice-tv</title> <script> var domain = 'sipjs.onsip.com'; var token = '42c3'; var d123 = new Date(); d123.setTime(d123.getTime() + 1000*60*60); // expires in 1 hour document.cookie = ('onsipToken=' + token + ';' + 'expires=' + d123.toUTCString() + ';'); var fromName = 'Alice'; var toName = 'Bob'; var fromURI = fromName.toLowerCase() + '.' + token + '@' + domain; var toURI = toName.toLowerCase() + '.' + token + '@' + domain; </script> <script src="https://rawgit.com/onsip/SIP.js/0.7.5/dist/sip-0.7.5.js"></script> <script src="demo.js"></script> </head> <body> <div class="content"> <div class="demo-window"> <div class="left"> <h4>Я Алиса</h4> <h5>В окне доктор Боб</h5> </div> <div class="demo-view"> <video id="video" muted="muted"></video> </div> <button id="video-button" class="right" type="button">video</button> <div class="clearfix"></div> <div id="content-message"> <div id="message-display"> <p class="message"><span class="message-from"></span> <span class="message-body placeholder">No messages yet</span></p> </div> <textarea id="message-input" class="message-input" placeholder="Enter your message here!"></textarea> <br> <button id="message-button" class="right" type="button">send message</button> </div> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>Bob-tv</title> <script> var domain = 'sipjs.onsip.com'; var token = '42c3'; var d123 = new Date(); d123.setTime(d123.getTime() + 1000*60*60); // expires in 1 hour document.cookie = ('onsipToken=' + token + ';' + 'expires=' + d123.toUTCString() + ';'); var fromName = 'Bob'; var toName = 'Alice'; var fromURI = fromName.toLowerCase() + '.' + token + '@' + domain; var toURI = toName.toLowerCase() + '.' + token + '@' + domain; </script> <script src="https://rawgit.com/onsip/SIP.js/0.7.5/dist/sip-0.7.5.js"></script> <script src="demo.js"></script> </head> <body> <div class="content"> <div class="demo-window"> <div class="left"> <h4>Я доктор Боб</h4> <h5>В окне вижу Алису</h5> </div> <div class="demo-view"> <video id="video" muted="muted"></video> </div> <button id="video-button" class="right" type="button">video</button> <div class="clearfix"></div> <div id="content-message"> <div id="message-display"> <p class="message"><span class="message-from"></span> <span class="message-body placeholder">No messages yet</span></p> </div> <textarea id="message-input" class="message-input" placeholder="Enter your message here!"></textarea> <br> <button id="message-button" class="right" type="button">send message</button> </div> </div> </div> </body> </html>
Скрипт не передает звук (компьютер не начнет орать, как телевизор). Чтобы разрешить audio, надо исправить false на true в 2-х местах:
строка 96: var options = mediaOptions(false, true, remoteRender, null);
строка 116-117: session = makeCall(userAgent, target,
false, true,
function createUA(callerURI, displayName) { var configuration = { traceSip: true, uri: callerURI, displayName: displayName }; var userAgent = new SIP.UA(configuration); return userAgent; } function setUpMessageInterface(userAgent, target, messageRenderId, messageInputId, buttonId) { var messageRender = document.getElementById(messageRenderId); var messageInput = document.getElementById(messageInputId); var button = document.getElementById(buttonId); function sendMessage() { var msg = messageInput.value; if (msg !== '') { messageInput.value = ''; userAgent.message(target, msg); } } var noMessages = true; userAgent.on('message', function (msg) { if (noMessages) { noMessages = false; if (messageRender.childElementCount > 0) messageRender.removeChild(messageRender.children[0]); } var msgTag = createMsgTag(msg.remoteIdentity.displayName, msg.body); messageRender.appendChild(msgTag); }); button.addEventListener('click', function () { sendMessage(); }); messageInput.onkeydown = (function(e) { if(e.keyCode == 13 && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); } function createMsgTag(from, msgBody) { var msgTag = document.createElement('p'); msgTag.className = 'message'; var fromTag = document.createElement('span'); fromTag.appendChild(document.createTextNode(from + ':')); var msgBodyTag = document.createElement('span'); msgBodyTag.appendChild(document.createTextNode(' ' + msgBody)); msgTag.appendChild(fromTag); msgTag.appendChild(msgBodyTag); return msgTag; } function mediaOptions(audio, video, remoteRender, localRender) { return { media: { constraints: { audio: audio, video: video }, render: { remote: remoteRender, local: localRender } } }; } function makeCall(userAgent, target, audio, video, remoteRender, localRender) { var options = mediaOptions(audio, video, remoteRender, localRender); var session = userAgent.invite('sip:' + target, options); return session; } function setUpVideoInterface(userAgent, target, remoteRenderId, buttonId) { var onCall = false; var session; var remoteRender = document.getElementById(remoteRenderId); var button = document.getElementById(buttonId); userAgent.on('invite', function (incomingSession) { onCall = true; session = incomingSession; var options = mediaOptions(false, true, remoteRender, null); button.firstChild.nodeValue = 'hang up'; session.accept(options); session.on('bye', function () { onCall = false; button.firstChild.nodeValue = 'video'; session = null; }); }); button.addEventListener('click', function () { if (onCall) { onCall = false; button.firstChild.nodeValue = 'video'; session.bye(); session = null; } else { onCall = true; button.firstChild.nodeValue = 'hang up'; session = makeCall(userAgent, target, false, true, remoteRender, null); session.on('bye', function () { onCall = false; button.firstChild.nodeValue = 'video'; session = null; }); } }); } //**************************** (function () { if (SIP.WebRTC.isSupported()) { window.fromUA = createUA(fromURI, fromName); var registrationFailed = false; var failRegistration = function () { registrationFailed = true; failInterfaceSetup(); }; fromUA.on('registered', setupInterfaces); fromUA.on('registrationFailed', failRegistration); window.onunload = function () { fromUA.stop(); }; function setupInterfaces() { setUpVideoInterface(fromUA, toURI, 'video', 'video-button'); setUpMessageInterface(fromUA, toURI, 'message-display', 'message-input', 'message-button'); } function failInterfaceSetup() { alert('Max registration limit hit. The app is disabled.'); } } })();
Для тех, кто будет устанавливать это на сервер и знаком с jinja2
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>{{fromName}}-tv</title> <script> var domain = 'sipjs.onsip.com'; var token = '{{token}}'; var d123 = new Date(); d123.setTime(d123.getTime() + 1000*60*60); // expires in 1 hour document.cookie = ('onsipToken=' + token + ';' + 'expires=' + d123.toUTCString() + ';'); var fromName = '{{fromName}}'; var toName = '{{toName}}'; var fromURI = fromName.toLowerCase() + '.' + token + '@' + domain; var toURI = toName.toLowerCase() + '.' + token + '@' + domain; </script> <script src="https://rawgit.com/onsip/SIP.js/0.7.5/dist/sip-0.7.5.js"></script> <script src="{{request.static_url('mydoctor:static/demo.js')}}"></script> </head> <body> <div class="content"> <div class="demo-window"> <div class="left"> <h4>{{title}}</h4> <h5>{{comment}}</h5> </div> <div class="demo-view"> <video id="video" muted="muted"></video> </div> <button id="video-button" class="right" type="button">video</button> <div class="clearfix"></div> <div id="content-message"> <div id="message-display"> <p class="message"><span class="message-from"></span> <span class="message-body placeholder">No messages yet</span></p> </div> <textarea id="message-input" class="message-input" placeholder="Enter your message here!"></textarea> <br> <button id="message-button" class="right" type="button">send message</button> </div> </div> </div> </body> </html>
_token = uuid.uuid4().hex ... { 'title': 'Я Алиса', 'comment': 'В окне доктор Боб', 'fromName':'Alice', 'toName':'Bob', 'token': _token } ... { 'title': 'Я доктор Боб', 'comment': 'В окне вижу Алису', 'fromName':'Bob', 'toName':'Alice', 'token': _token }
Уникальные расширения имен для Алисы и Боба сделает сервер при загрузке.
Я попытался установить свой SIP-сервер. Выбрал FreeSWITCH, день разбирался с настройками, но так и не смог настроить: голос поступал с задержкой в 5 (пять) секунд, видео зависало со странной записью в логе FS о том, что видео-формат не поддерживается.
Текст и файлы пересылались нормально. Похоже FS все пытался пропустить через себя (видео-конференцию учинить или для других целей).
Надеюсь, что какой-нибудь спец по SIP/STUN улыбнется, вколотит мне минус за неумейство и объяснит, какой SIP/STUN выбрать и как его настроить.
