Я работаю с 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 выбрать и как его настроить.