И решили мы подружить 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 агент с большим набором кодеков и различных модулей, вот только интерфейс взаимодействия подкачал :)

© Habrahabr.ru