И решили мы подружить Baresip и Nodejs
Что же такое Baresip? Как сказано на сайте проекта это кроссплатформенный, модульный SIP агент с поддрежкой аудио и видео.
А причем тут nodejs? Да так вышло что в большом проекте написанном на nodejs и использующем Freeswitch и его модуль portaudio в связи с некорректной работой последнего потребовалось внедрить другой sip клиент для аудио звонков.
Выбор в ходе технического совещания одного из наших разработчиков с гуглом пал на Baresip.
Основные задачи:
- Управление звонками(позвонить, положить трубку, ответить и т.д.)
- Получение событий(входящий звонок, звонок завершился, дозвонились и т.д.)
- Получение информации о состоянии клиента(список звонков и т.д.)
В результате получился небольшой npm модуль node_baresip
Ссылка на страницу baresip
Поиск api
Так как baresip консольная утилита я решил что наверника есть или библиотека или какойто способ взаимодействия с запущенным приложением.
И я был прав но не до конца :)
Как оказалось в baresip все завязано на текстовые команды и их можно ввести через:
- консоль
- get параметр http запроса
- UDP/TCP соединение
Настоящий хардкорный api :)
Для первой реализации я выбрал http.
Реализация node_baresip
Отправка команд будет выглядеть так:
Звонок в консоли
d sip:user@domain.com
Звнок через http
http://127.0.0.1:8000/?duser@domain.com
Был написан baresiphttpwrapper.js с обобщенным интерфейсом:
- getCallList
- dial
- hangup
- answer
- muteUnmute
- hold
- resume
В последствии можно либо написать нормальный api и сделать другой wrapper либо дождаться когда кто-то его напишет и переписать wrapper :)
Далее реализован основной файл модуля baresip.js
Для отслеживания состояния приходиться запрашивать список звонков каждые 2 секунды и определять какие звонки старые, какие были заверешны, какие исходящие дозвонились и какие входящие и выдавать соответсвующие события:
self.intervalObject = setInterval( function(){
self.baresipWrapper.getCallList( function( err, callList){
if( err){
console.log( "Error get call list:", err, config);
var callList = [];
}
var esatblishedCalls = self.findEsatblishedCalls( callList);
var newCalls = self.findNewCalls( callList);
var deleteCalls = self.findDeletedCalls( callList);
for (var i in newCalls){
// ADD CALL
self.callList[newCalls[i].sip] = newCalls[i];
self.callList[newCalls[i].sip].id = self.generateCallId();
self.emit( "new_call", self.callList[newCalls[i].sip]);
if( newCalls[i].status == "INCOMING") {
self.emit("incoming_call", newCalls[i]);
}
else if( newCalls[i].status == "ESTABLISHED"){
self.emit( "established_call", self.callList[newCalls[i].sip]);
}
}
for (var i in deleteCalls){
delete self.callList[deleteCalls[i].sip];
self.emit( "end_call", deleteCalls[i]);
}
for( var i in esatblishedCalls) {
self.callList[esatblishedCalls[i].sip].status = "ESTABLISHED";
self.emit( "established_call", esatblishedCalls[i]);
}
});
}, self.callListMonitorTimeout);
События
- new_call
- established_call
- end_call
Интерефейс управления я сделал практически такой же как во wrapper-е
- getCurrentCalls
- dial
- hangup
- answer
- muteUnmute
- hold
- resume
Веселые сюрпризы baresip
- Как оказалось в baresip у звонка нет никакого идентификатора следовательно и управлять им невозможно.
Все управление сделано только над последним звонком добавленным в с-шный список звонков в baresip. Доходит до того что когда приходит входящий звонок и уже есть актиынй зконок управлять можно только входящим а активный нельзя даже поставить на hold. - Одновременно может быть активно сколько угодно звонков, нет автоматической постановки активного звонка на hold при ответе на новый входящий и даже нет возможности опционально задавать это в конфиге.
- Можно сделать сколько угодно звонков одному и тому же абоненту.
Если последние 2 можно решить простыми проверками то первая заняла определенное время.
Сначало я попытался ввести в код baresip идентификатор звонка для возможности управления любым звонком через api, но так как Си я знаю плохо реализовать кроссплатформенный код который может генерировать уникальные идентификаторы я не смог.
Было принято более простое решение. Я добавил команду постановки предыдущего звонка на hold ее даже добавили в сам код baresip. Это мелкая правка решила стоявшие проблемы и дальше углубляться не пришлось.
Впечатления
Сам baresip оказался очень даже надежным кроссплатформенным sip агент с большим набором кодеков и различных модулей, вот только интерфейс взаимодействия подкачал :)