Доброго времени суток, хабравчане.
Сегодня я расскажу вам о работе 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, так как он удобен для отображения различных динамических объектов, имеющих большое количество внутренних состояний.
В основе клиента используется две основные сущности, это SIPml.Stack и SIPml.Session.
SIPml.Stack является управляющим потоком данных. Данный объект будет выступать в качестве основного при создании сессий с сервером.
SIPml.Session является потоком sip-сеанса с сервером. Используется непосредственно для передачи данных между сервером и клиентской частью.
Алгоритм работы с sipML5:
Инициализация библиотеки:
Создание Stack
Stack является ключевым источником данных для работы клиента.
При создании stack необходимо указывать большое количество конфигурационных данных, часть из которых является опциональной. Полную информацию по stack можно посмотреть здесь
После создания stack-объекта необходимо его запустить:
Важно отметить, что функция start() является асинхронной, поэтому, перед тем как начать совершать звонки, необходимо дождаться события запуска stack.
Полный перечень событий представлен тут много, и перечислять их все не имеет смысла.
После события создания stack необходимо создать сессию регистрации:
В ходе этой операции происходит sip запрос для входа пользователя в систему.
После выполнения данных действий мы готовы совершать или принимать звонки от пользователей.
Прием звонков
Входящий звонок в sipML5 инициализирует событие ”i_new_call” объекта SIPml.Stack.
В общем случае обработчик ответа на звонок выглядит следующим образом:
e.newSession — содержит в себе дополнительный объект сеанса связи, для события “i_new_call” это будет объект SIPml.Session.Call.
Функция accept() необходима для принятия звонка. В качестве параметра принимает объект SIPml.Session.Configuration
Инициализация звонков
Инициализация звонка сводится к созданию новой сессии типа SIPml.Session.Call
После инициализации можно приступать к вызову абонента, через его идентификатор, номер или url (e.g. 'sip:johndoe@example.com' or 'johndoe' or '+33600000000').:
Управление звонком
Управление звонком это методы класса SIPml.Session.Call
Основные из них:
● .hangup() — завершение звонка
● .hold() / .resume() — удержание/возобновление звонка
● .mute(media, mute) — отключение входящего звука, где media — тип контента для отключения; mute — boolean — активация/деактивация mute.
RtlsSip.callSession.mute('audio', true);
Есть также множество других функций, информацию о которых вы найдете здесь.
Схема приложения имеет достаточно простую архитектуру. В качестве идеологии однонаправленность данных и отсутствия взаимных зависимостей между элементами.

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