Доброго времени суток, читатели.
Сегодня я расскажу вам о работе sip-телефонии, а именно о том, как я организовывал звуковой сеанс между мобильными рациями (или ИКН) о которых вы слышали ранее из других статей нашей компании и web-клиентом через webRTC с использованием sipML5 в качестве библиотеки и asterisk 11 в качестве АТС.
Всем кому небезразлична данная тема — добро пожаловать под кат.
Немного предыстории
К нам в отдел поступило оперативное задание разработать sip-клиент, который работал бы на asterisk, и как минимум на google chrome (как мобильной, так и десктопной его версии). С этого и закрутилось.
В системе локального позиционирования RealTrac существуют мобильные устройства типа рация, или, как мы их называем, ИКН (интерком носимый), который обеспечивает использование как дуплексной связи, так и широковещательной полудуплексной связи с другими устройствами. Эти устройства взаимодействуют с коммуникационным сервером INCPd, основная задача которого – обработка пакетов протокола INCP, посредством которого обеспечивается обмен информацией с устройствами системы RealTrac. В том числе, данный сервер обрабатывает поступающий голосовой трафик, и перепаковывает его в sip-пакеты, для дальнейшей работы со сторонним софтом.
В качестве коммутатора выступает asterisk.
Инструментарий
SipML5 был выбран нами неслучайно. Во-первых, данная библиотека хорошо подходит для интеграции с asterisk. Во-вторых, мы уже имели опыт работы с подобными системами, однако, из-за того, что наша компания поддерживала Debian 7 в качестве ОС для нашего программного обеспечения имелись определенные сложности. В дистрибутиве Debian 7 доступен только asterisk 1.8 в то время как webRTC без танцев с бубном, в виде webrtc2sip, работает только с 11 версии asterisk, что нас несильно устраивало, так как это могло бы стать полигоном проблем для будущих инсталляций.
С релизом Debian 8 было решено продолжить движение в направлении данного функционала.
В качестве инструментария создания ui-части выступал React.js, так как он удобен для отображения различных динамических объектов, имеющих большое количество внутренних состояний.
Небольшое введение по sipML5
В основе клиента используется две основные сущности, это SIPml.Stack и SIPml.Session.
SIPml.Stack является управляющим потоком данных. Данный объект будет выступать в качестве основного при создании сессий с сервером.
SIPml.Session является потоком sip-сеанса с сервером. Используется непосредственно для передачи данных между сервером и клиентской частью.
Алгоритм работы с sipML5:
Инициализация библиотеки:
SIPml.init(readyCallback, errorCallback);
Создание Stack
Stack является ключевым источником данных для работы клиента.
RtlsSip.stack = new SIPml.Stack({
realm: Data.property.realm,
impi: Data.property.impi,
impu: Data.property.impu,
password: Data.property.password,
display_name: Data.property.display_name,
websocket_proxy_url: Data.property.websocket_proxy_url,
events_listener: { events: '*', listener: eventsListener },
sip_headers: [
{ name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.0.0' },
{ name: 'Organization', value: 'RTLS' }
]
})
При создании stack необходимо указывать большое количество конфигурационных данных, часть из которых является опциональной. Полную информацию по stack можно посмотреть здесь
После создания stack-объекта необходимо его запустить:
RtlsSip.stack.start();
Важно отметить, что функция start() является асинхронной, поэтому, перед тем как начать совершать звонки, необходимо дождаться события запуска stack.
Полный перечень событий представлен тут много, и перечислять их все не имеет смысла.
После события создания stack необходимо создать сессию регистрации:
RtlsSip.stack.newSession('register', {
events_listener: { events: '*', listener: registerListeners} });
RtlsSip.sessions.register();
В ходе этой операции происходит sip запрос для входа пользователя в систему.
После выполнения данных действий мы готовы совершать или принимать звонки от пользователей.
Прием звонков
Входящий звонок в sipML5 инициализирует событие ”i_new_call” объекта SIPml.Stack.
В общем случае обработчик ответа на звонок выглядит следующим образом:
var eventCallback = function(е){
RtlsSip.callSession = e.newSession;
RtlsSip.callSession.events_listener({events: '*', listener: RtlsSip.sessionEventListener})
RtlsSip.callSession.accept(
{audio_remote: document.getElementById('audio_remote'),
events_listener: { events: '*', listener: RtlsSip.sessionEventListener}})
}
e.newSession — содержит в себе дополнительный объект сеанса связи, для события “i_new_call” это будет объект SIPml.Session.Call.
Функция accept() необходима для принятия звонка. В качестве параметра принимает объект SIPml.Session.Configuration
Инициализация звонков
Инициализация звонка сводится к созданию новой сессии типа SIPml.Session.Call
RtlsSip.callSession = RtlsSip.stack.newSession('call-audio', {
audio_remote: document.querySelector('#audio_remote'),
events_listener: { events: '*', listener: RtlsSip.sessionEventListener }
});
После инициализации можно приступать к вызову абонента, через его идентификатор, номер или url (e.g. 'sip:johndoe@example.com' or 'johndoe' or '+33600000000').:
RtlsSip.callSession.call(number);
Управление звонком
Управление звонком это методы класса SIPml.Session.Call
Основные из них:
● .hangup() — завершение звонка
● .hold() / .resume() — удержание/возобновление звонка
● .mute(media, mute) — отключение входящего звука, где media — тип контента для отключения; mute — boolean — активация/деактивация mute.
RtlsSip.callSession.mute('audio', true);
Есть также множество других функций, информацию о которых вы найдете здесь.
Схема приложения:
Схема приложения имеет достаточно простую архитектуру. В качестве идеологии однонаправленность данных и отсутствия взаимных зависимостей между элементами.
Интеграция в систему
Настройки для asterisk сервера я здесь описывать не буду, так как их несложно найти на просторах сети.
Для абонентов типа web-client был выделен собственный список номеров. Номера из пула устанавливаются диспетчерам при создании аккаунта пользователя.
Диспетчер может совершать звонки как в полудуплексном, так и в дуплексном режиме, а также связываться с другими диспетчерами в случае необходимости.
Устройство по умолчанию звонит в полудуплексном режиме. Диспетчер, которому звонит устройство выбирается в зависимости от геосегмента в котором находится устройство в текущий момент времени.
Все это позволяет добиться максимального комфорта в использовании системы.
Итоги:
На данный момент, эта технология проходит у нас процесс интеграции и тестирования. Уверен, что в процессе работы найдется немало помех и проблем.
Вполне возможно, что в будущем мы попробуем решение на основании других технологий, предоставляющих тот же функционал.
by Sinires
Автор: RTL-Service