У FreePBX есть Web Sip клиент. Мне было интересно как он работает. Начав изучать технологию WebRTC я наткнулся на 3 библиотеки — это JsSIP, его fork SIP.js, а также sipml5.
JsSIP и SIP.js не удалось запустить с первого раза, не хватало опыта. А вот sipml5 оказался самым простым в применении. Скачав его тестовую версию, получилось совершить первый звонок.
Все 3 библиотеки работоспособны. У каждой есть свои сильные и слабые стороны. Протестировав все 3 библиотеки, остановил выбор на JsSIP.
Основные плюсы и минусы с которыми я столкнулся:
1. JsSIP
Плюсы:
- легковесная;
- активно поддерживается разработчиками.
Минусы:
- на начальном этапе более сложная для старта;
- пришлось править исходники чтобы заработал с asterisk.
2. Sip.js
Плюсы:
- легковесная;
- упор делается для работы с asterisk.
Минусы:
- не удалось запустить звук для Early media (не подключается звук для предответных сообщении оператора).
3. Sipml5
Плюсы:
- относительно простая для новичков.
Минусы:
- сложный и запутанный код, не было желания разбираться в нем.
Подготовка
Веб клиент работает только на странице с поддержкой шифрования. Нам нужно создать самоподписанный сертификат. Как это делается есть готовые статьи в Интернете.
Firefox разрешает работать с самоподписанными сертификатами. Для работы в Хроме придется делать сначала корневой сертификат, а на его основе уже подписывать сертификат для работы телефона. Корневой сертификат должен быть импортирован в хранилище сертификатов.
Включение поддержки web sip клиента на FreePBX
Администратор -> User Managment -> Вкладка «Groups» -> Вкладка «UCP» -> Вкладка «Phone»
Для свойства «Enable Phone» выбираем значение «Да».
Если у вас в FreePBX были внутренние номера, допустим 200, то при активации web sip клиента появятся дополнительные номера с префиксом 99, т.е. 99200.
На клиентском браузере, после того как дали разрешение на использование самоподписанного сертификата, нужно еще дать разрешение для сертификата asterisk. Для этого наберите в браузере
https://192.168.1.70:8089/ws
IP-адрес нужно указать тот, на котором запущен asterisk.
Правка исходника JsSIP
В исходниках библиотеки JsSIP необходимо заменить transport=ws на transport=wss. Без этой правки клиент не заработает. В Sip.js править ничего не нужно, там этот момент предусмотрели.
Код web sip клиента на основе JsSIP
Для демонстрации работы клиента скопируйте нижеприведенный код, сохраните и запустите страницу. Страница демонстрирует исходящий звонок. Код 100% рабочий, проверил прежде чем выложить.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="/lib/jquery-3.4.1.js"></script>
<script type="text/javascript" src="/lib/jsSIP/jssip.min.js"></script>
</head>
<body>
<style>
#callControl {
display:flex;
flex-direction:column;
justify-content:flex-start;
align-items: center;
}
#num {
margin-top: 20px;
padding-left: 10px;
font-size: 1em;
font-family:'Open Sans', sans-serif;
width: 300px;
height: 40px;
border:0px;
border-bottom: 1px solid #B8B8B8;
}
#call {
display:flex;
background-color: #389400;
}
#hangup {
display:none;
background-color:#A90002;
}
#call, #hangup {
flex-direction:column;
justify-content:center;
align-items: center;
color: #FFF;
width:260px;
height: 40px;
font-size: 20px;
margin: 20px;
border-radius: 20px;
cursor: pointer;
}
</style>
<div id="callControl">
<h2>Web Sip клиент на основе JsSIP</h2>
<div id="to">
<input id="num" type="text" placeholder="Введите номер телефона"/>
</div>
<div id="call">Вызов</div>
<div id="hangup">Завершить</div>
</div>
</body>
<script>
JsSIP.debug.enable('JsSIP:*');
var socket = new JsSIP.WebSocketInterface('wss://192.168.1.70:8089/ws');
var configuration = {
sockets : [ socket ],
// внутренний номер
uri : '99XXX@192.168.1.70',
// пароль
password : 'passw'
};
var remoteAudio = new window.Audio();
remoteAudio.autoplay = true;
var ua = new JsSIP.UA(configuration);
// События регистрации клиента
ua.on('connected', function(e) { /* Ваш код */ });
ua.on('disconnected', function(e) { /* Ваш код */ });
ua.on('registered', function(e) { /* Ваш код */ });
ua.on('unregistered', function(e) { /* Ваш код */ });
ua.on('registrationFailed', function(e) { /* Ваш код */ });
// Запускаем
ua.start();
// Обработка событии исх. звонка
var eventHandlers = {
'progress': function(e) {
console.log('call is in progress');
session.connection.ontrack = function(e) {
console.log(e);
remoteAudio.srcObject = e.streams[0];
};
},
'failed': function(e) {
console.log('call failed with cause: ' + e.cause);
$('#call').css({'display' : 'flex'});
$('#hangup').css({'display' : 'none'});
},
'ended': function(e) {
console.log('call ended with cause: ' + e.cause);
$('#call').css({'display' : 'flex'});
$('#hangup').css({'display' : 'none'});
},
'confirmed': function(e) {
console.log('call confirmed');
console.log(e);
}
};
var options = {
'eventHandlers' : eventHandlers,
'mediaConstraints' : { 'audio': true, 'video': false }
};
// Кнопка для звонка
$('#call').click(function(e) {
session = ua.call($('#num').val(), options);
$('#call').css({'display' : 'none'});
$('#hangup').css({'display' : 'flex'});
});
// Кнопка для отбоя звонка
$('#hangup').click(function() {
if (session) {
session.terminate();
}
$('#call').css({'display' : 'flex'});
$('#hangup').css({'display' : 'none'});
});
</script>
</html>
Должна получиться такая страница:
Теперь можно пробовать совершить первый звонок. Если все сделали правильно, то услышите гудок, при ответе должен пойти звук в обе стороны. Начинаем полноценную беседу.
Итоги
За последние 2 года использования Web sip клиента были проблемы, которые появлялись при обновлении браузера.
Например в 63 версии Firefox пришлось менять SDP протокол, чтобы сессия не отбивалась. Браузер перестал держать связь без параметра a=mid:0. В последних версиях уже не актуально, ошибку исправили, в более поздних версиях уже нет такой проблемы.
В 65 версии Firefox при исх. звонке начал пропадать голос собеседника, в локалке не было таких проблем. Проблема только при вызове наружу через шлюз. В хроме такой проблемы не наблюдалось, все работает стабильно.
В основном пользователи работают в Firefox, и по этой причине пришлось в библиотеке убрать повторное согласование SDP протокола. Проблемы с пропаданием звука после ответа перестали беспокоить.
Перспективы
В данный момент на js была написана программа «Автообзвон», которая способна по первым нескольким секундам распознавать предответные сообщения оператора такие как: тел. не существует, абон. занят, тел. отключен, тел. доступен. Точность распознавания 95% и выше. Если будет время, напишу статью и опишу алгоритм.
Автор: AidynTM