Использование webrtc для взаимодействия с asterisk-сервером или как заставить рации говорить с браузером

Опубликовано Fox - сб, 06/11/2016 - 18:24

Источник.

Доброго времени суток, хабравчане.
Сегодня я расскажу вам о работе sip-телефонии, а именно о том, как я организовывал звуковой сеанс между мобильными рациями (или ИКН) о которых вы слышали ранее из других статей нашей компании и web-клиентом через webRTC с использованием sipML5 в качестве библиотеки и asterisk 11 в качестве АТС. 
image
Всем кому небезразлична данная тема — добро пожаловать под кат.
 

Немного предыстории

К нам в отдел поступило оперативное задание разработать sip-клиент, который работал бы на asterisk, и как минимум на google chrome (как мобильной, так и десктопной его версии). С этого и закрутилось.

В системе локального позиционирования RealTrac существуют мобильные устройства типа рация, или, как мы их называем, ИКН (интерком носимый), который обеспечивает использование как дуплексной связи, так и широковещательной полудуплексной связи с другими устройствами. Эти устройства взаимодействуют с коммуникационным сервером INCPd, основная задача которого – обработка пакетов протокола INCP, посредством которого обеспечивается обмен информацией с устройствами системы RealTrac. В том числе, данный сервер обрабатывает поступающий голосовой трафик, и перепаковывает его в sip-пакеты, для дальнейшей работы со сторонним софтом.
В качестве коммутатора выступает asterisk.

image
 

Инструментарий

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);

Есть также множество других функций, информацию о которых вы найдете здесь.
 

Схема приложения:

Схема приложения имеет достаточно простую архитектуру. В качестве идеологии однонаправленность данных и отсутствия взаимных зависимостей между элементами. 

image
 

Интеграция в систему

Настройки для asterisk сервера я здесь описывать не буду, так как их несложно найти на просторах сети.
Для абонентов типа web-client был выделен собственный список номеров. Номера из пула устанавливаются диспетчерам при создании аккаунта пользователя. 
Диспетчер может совершать звонки как в полудуплексном, так и в дуплексном режиме, а также связываться с другими диспетчерами в случае необходимости. 
Устройство по умолчанию звонит в полудуплексном режиме. Диспетчер, которому звонит устройство выбирается в зависимости от геосегмента в котором находится устройство в текущий момент времени. 
Все это позволяет добиться максимального комфорта в использовании системы. 
Итоги:
На данный момент, эта технология проходит у нас процесс интеграции и тестирования. Уверен, что в процессе работы найдется немало помех и проблем. 
Вполне возможно, что в будущем мы попробуем решение на основании других технологий, предоставляющих тот же функционал. 

by Sinires