Browse Source

Refactor PJSUA callbacks.

Michał Słomkowski 8 years ago
parent
commit
3267329a63
3 changed files with 146 additions and 114 deletions
  1. 7 1
      .gitignore
  2. 118 88
      PjsuaCommunicator.cpp
  3. 21 25
      PjsuaCommunicator.hpp

+ 7 - 1
.gitignore

@@ -3,4 +3,10 @@
 
 config.ini
 
-build/
+build/
+
+CMakeCache.txt
+CMakeFiles/
+Makefile
+cmake_install.cmake
+mumsi

+ 118 - 88
PjsuaCommunicator.cpp

@@ -11,68 +11,56 @@ using namespace std;
 /**
  * These are global, because there's no way to pass it's value to onCallMediaState callback.
  */
-static int mediaPortSlot = -1;
-static sip::PjsuaCommunicator *pjsuaCommunicator = nullptr;
+//static int mediaPortSlot = -1;
+//static sip::PjsuaCommunicator *pjsuaCommunicator = nullptr;
 
 static log4cpp::Category &pjLogger = log4cpp::Category::getInstance("PjSip");
 
 
-void sip::PjsuaCommunicator_onCallMediaState(pjsua_call_id call_id) {
+void sip::PjsuaCommunicator::onCallMediaState(pjsua_call_id call_id) {
     pjsua_call_info ci;
 
     pjsua_call_get_info(call_id, &ci);
 
     if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
-
-        if (mediaPortSlot == -1) {
-            pj_status_t status = pjsua_conf_add_port(
-                    pjsuaCommunicator->pool,
-                    pjsuaCommunicator->mediaPort,
-                    &mediaPortSlot);
-
-            if (status != PJ_SUCCESS) {
-                throw sip::Exception("error when calling pjsua_conf_add_port", status);
-            }
-        }
-
         pjsua_conf_connect(ci.conf_slot, mediaPortSlot);
         pjsua_conf_connect(mediaPortSlot, ci.conf_slot);
     }
 }
 
-static void onIncomingCall(pjsua_acc_id acc_id,
-                           pjsua_call_id call_id,
-                           pjsip_rx_data *rdata) {
+void sip::PjsuaCommunicator::onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata) {
     pjsua_call_info ci;
 
-    PJ_UNUSED_ARG(acc_id);
     PJ_UNUSED_ARG(rdata);
 
     pjsua_call_get_info(call_id, &ci);
-    pjsua_call_set_user_data(call_id, pjsuaCommunicator);
+    pjsua_call_set_user_data(call_id, this);
 
-    pjLogger.info("Incoming call from %s.", ci.remote_info.ptr);
+    logger.info("Incoming call from %s.", ci.remote_info.ptr);
 
-    /* Automatically answer incoming calls with 200/OK */
-    pjsua_call_answer(call_id, 200, NULL, NULL);
-}
+    if (this->available) {
+        available = false;
 
-static void onDtmfDigit(pjsua_call_id callId, int digit) {
+        pjsua_call_set_user_data(call_id, this);
+        pjsua_call_answer(call_id, 200, nullptr, nullptr);
+    } else {
+        // 486 Busy Here - Callee is Busy
+        pjsua_call_answer(call_id, 486, nullptr, nullptr);
+    }
+}
 
-    pjLogger.notice("DTMF digit '%c' (call %d).", digit, callId);
+void sip::PjsuaCommunicator::onDtmfDigit(pjsua_call_id callId, int digit) {
+    logger.notice("DTMF digit '%c' (call %d).", digit, callId);
 }
 
-static void onCallState(pjsua_call_id call_id,
-                        pjsip_event *e) {
+void sip::PjsuaCommunicator::onCallState(pjsua_call_id call_id, pjsip_event *e) {
     pjsua_call_info ci;
 
     PJ_UNUSED_ARG(e);
 
     pjsua_call_get_info(call_id, &ci);
-    sip::PjsuaCommunicator *communicator
-            = reinterpret_cast<sip::PjsuaCommunicator *>(pjsua_call_get_user_data(call_id));
 
-    pjLogger.info("Call %d state=%s.", call_id, ci.state_text.ptr);
+    logger.info("Call %d state=%s.", call_id, ci.state_text.ptr);
 
     string address = string(ci.remote_info.ptr);
 
@@ -81,15 +69,54 @@ static void onCallState(pjsua_call_id call_id,
 
     if (ci.state == PJSIP_INV_STATE_CONFIRMED) {
         auto msgText = "Start call from " + address + ".";
-        pjLogger.notice(msgText);
-        communicator->onStateChange(msgText);
+
+        logger.notice(msgText);
+        onStateChange(msgText);
     } else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
         auto msgText = "End call from " + address + ".";
-        pjLogger.notice(msgText);
-        communicator->onStateChange(msgText);
+
+        logger.notice(msgText);
+        onStateChange(msgText);
+
+        available = true;
     }
 }
 
+static void callback_onCallMediaState(pjsua_call_id callId) {
+    auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_call_get_user_data(callId));
+    communicator->onCallMediaState(callId);
+}
+
+static void callback_onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata) {
+    auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_acc_get_user_data(acc_id));
+    communicator->onIncomingCall(acc_id, call_id, rdata);
+}
+
+static void callback_onDtmfDigit(pjsua_call_id callId, int digit) {
+    auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_call_get_user_data(callId));
+    communicator->onDtmfDigit(callId, digit);
+}
+
+static void callback_onCallState(pjsua_call_id call_id, pjsip_event *e) {
+    auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_call_get_user_data(call_id));
+    communicator->onCallState(call_id, e);
+}
+
+static pj_status_t callback_getFrame(pjmedia_port *port, pjmedia_frame *frame) {
+    auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
+    return communicator->mediaPortGetFrame(port, frame);
+}
+
+static pj_status_t callback_putFrame(pjmedia_port *port, pjmedia_frame *frame) {
+    auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
+    return communicator->mediaPortPutFrame(port, frame);
+}
+
+static pj_status_t callback_onDestroy(pjmedia_port *port) {
+    auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
+    return communicator->_mediaPortOnDestroy(port);
+}
+
 static void pjLogToLog4CppBridgeFunction(int level, const char *data, int len) {
     using namespace log4cpp;
     std::map<int, Priority::Value> prioritiesMap = {
@@ -113,10 +140,6 @@ sip::PjsuaCommunicator::PjsuaCommunicator()
           callbackLogger(log4cpp::Category::getInstance("SipCommunicatorCallback")) {
     pj_status_t status;
 
-    if (pjsuaCommunicator != nullptr) {
-        throw sip::Exception("this is a singleton class");
-    }
-    pjsuaCommunicator = this;
 
     status = pjsua_create();
     if (status != PJ_SUCCESS) {
@@ -133,10 +156,10 @@ sip::PjsuaCommunicator::PjsuaCommunicator()
     generalConfig.user_agent = toPjString(userAgent);
     generalConfig.max_calls = 1;
 
-    generalConfig.cb.on_incoming_call = &onIncomingCall;
-    generalConfig.cb.on_dtmf_digit = &onDtmfDigit;
-    generalConfig.cb.on_call_media_state = &PjsuaCommunicator_onCallMediaState;
-    generalConfig.cb.on_call_state = &onCallState;
+    generalConfig.cb.on_incoming_call = &callback_onIncomingCall;
+    generalConfig.cb.on_dtmf_digit = &callback_onDtmfDigit;
+    generalConfig.cb.on_call_media_state = &callback_onCallMediaState;
+    generalConfig.cb.on_call_state = &callback_onCallState;
 
     pjsua_logging_config logConfig;
     pjsua_logging_config_default(&logConfig);
@@ -145,26 +168,31 @@ sip::PjsuaCommunicator::PjsuaCommunicator()
 
     pjsua_media_config mediaConfig;
     pjsua_media_config_default(&mediaConfig);
-    mediaConfig.max_media_ports = 3;
+    mediaConfig.no_vad = 1;
 
     status = pjsua_init(&generalConfig, &logConfig, &mediaConfig);
     if (status != PJ_SUCCESS) {
         throw sip::Exception("error in pjsua_init", status);
     }
 
-    status = pjsua_set_null_snd_dev();
-    if (status != PJ_SUCCESS) {
-        throw sip::Exception("error setting null device", status);
-    }
-
     pj_caching_pool cachingPool;
     pj_caching_pool_init(&cachingPool, &pj_pool_factory_default_policy, 0);
     pool = pj_pool_create(&cachingPool.factory, "wav", 32768, 8192, nullptr);
 
     // todo calculate sizes
-    pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff);
+    status = pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff);
+    if (status != PJ_SUCCESS) {
+        throw sip::Exception("error when creating circular buffer", status);
+    }
+
+    logger.notice("Active ports: %d.", pjsua_conf_get_active_ports());
 
-    mediaPort = createMediaPort();
+    createMediaPort();
+
+    status = pjsua_conf_add_port(pool, &mediaPort, &mediaPortSlot);
+    if (status != PJ_SUCCESS) {
+        throw sip::Exception("error when calling pjsua_conf_add_port", status);
+    }
 }
 
 void sip::PjsuaCommunicator::connect(
@@ -182,12 +210,17 @@ void sip::PjsuaCommunicator::connect(
 
     status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transportConfig, NULL);
     if (status != PJ_SUCCESS) {
-        throw sip::Exception("Error creating transport", status);
+        throw sip::Exception("error creating transport", status);
     }
 
     status = pjsua_start();
     if (status != PJ_SUCCESS) {
-        throw sip::Exception("Error starting sip", status);
+        throw sip::Exception("error starting PJSUA", status);
+    }
+
+    status = pjsua_set_null_snd_dev();
+    if (status != PJ_SUCCESS) {
+        throw sip::Exception("error in pjsua_set_null_std_dev()", status);
     }
 
     registerAccount(host, user, password);
@@ -195,20 +228,16 @@ void sip::PjsuaCommunicator::connect(
 
 sip::PjsuaCommunicator::~PjsuaCommunicator() {
     pjsua_destroy();
-
-    if (mediaPort != nullptr) {
-        delete mediaPort;
-    }
 }
 
-pjmedia_port *sip::PjsuaCommunicator::createMediaPort() {
+void sip::PjsuaCommunicator::createMediaPort() {
 
-    pjmedia_port *mp = new pjmedia_port();
+    eof = false;
 
-    string name = "PjsuaMP";
+    string name = "PjsuamediaPort";
     auto pjName = toPjString(name);
 
-    pj_status_t status = pjmedia_port_info_init(&(mp->info),
+    pj_status_t status = pjmedia_port_info_init(&(mediaPort.info),
                                                 &pjName,
                                                 PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'),
                                                 SAMPLING_RATE,
@@ -221,39 +250,23 @@ pjmedia_port *sip::PjsuaCommunicator::createMediaPort() {
         throw sip::Exception("error while calling pjmedia_port_info_init().", status);
     }
 
-    mp->get_frame = &MediaPort_getFrameRawCallback;
-    mp->put_frame = &MediaPort_putFrameRawCallback;
+    mediaPort.port_data.pdata = this;
 
-    mp->port_data.pdata = this;
-
-    return mp;
+    mediaPort.get_frame = &callback_getFrame;
+    mediaPort.put_frame = &callback_putFrame;
+    mediaPort.on_destroy = &callback_onDestroy;
 }
 
-pj_status_t sip::MediaPort_getFrameRawCallback(pjmedia_port *port,
-                                               pjmedia_frame *frame) {
-    PjsuaCommunicator *communicator = static_cast<PjsuaCommunicator *>(port->port_data.pdata);
-    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_port *port, pjmedia_frame *frame) {
+    std::unique_lock<std::mutex> lock(inBuffAccessMutex);
 
-    return communicator->mediaPortGetFrame(frame);
-}
+    if (this->eof) {
+        return PJ_EEOF;
+    }
 
-pj_status_t sip::MediaPort_putFrameRawCallback(pjmedia_port *port,
-                                               pjmedia_frame *frame) {
-    PjsuaCommunicator *communicator = static_cast<PjsuaCommunicator *>(port->port_data.pdata);
-    pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
-    pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info);
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
-
-    communicator->mediaPortPutFrame(samples, count);
-
-    return PJ_SUCCESS;
-}
-
-pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_frame *frame) {
-    std::unique_lock<std::mutex> lock(inBuffAccessMutex);
-
     pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
-    pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&mediaPort->info);
+    pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&(port->info));
 
     pj_size_t availableSamples = pjmedia_circ_buf_get_len(inputBuff);
     const int samplesToRead = std::min(count, availableSamples);
@@ -273,11 +286,26 @@ pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_frame *frame) {
     return PJ_SUCCESS;
 }
 
-void sip::PjsuaCommunicator::mediaPortPutFrame(pj_int16_t *samples, pj_size_t count) {
+pj_status_t sip::PjsuaCommunicator::mediaPortPutFrame(pjmedia_port *port, pjmedia_frame *frame) {
+    if (this->eof) {
+        return PJ_EEOF;
+    }
+
+    pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
+    pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info);
+    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
     if (count > 0) {
         callbackLogger.debug("Calling onIncomingPcmSamples with %d samples.", count);
         onIncomingPcmSamples(samples, count);
     }
+
+    return PJ_SUCCESS;
+}
+
+pj_status_t sip::PjsuaCommunicator::_mediaPortOnDestroy(pjmedia_port *port) {
+    eof = true;
+    return PJ_SUCCESS;
 }
 
 void sip::PjsuaCommunicator::registerAccount(string host, string user, string password) {
@@ -307,6 +335,7 @@ void sip::PjsuaCommunicator::registerAccount(string host, string user, string pa
     accConfig.cred_info[0].username = toPjString(user);
     accConfig.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
     accConfig.cred_info[0].data = toPjString(password);
+    accConfig.user_data = this;
 
     pjsua_acc_id acc_id;
 
@@ -321,3 +350,4 @@ void sip::PjsuaCommunicator::sendPcmSamples(int16_t *samples, unsigned int lengt
     callbackLogger.debug("Pushing %d samples to in-buff.", length);
     pjmedia_circ_buf_write(inputBuff, samples, length);
 }
+

+ 21 - 25
PjsuaCommunicator.hpp

@@ -13,6 +13,7 @@
 #include <string>
 #include <stdexcept>
 #include <mutex>
+#include <climits>
 
 namespace sip {
 
@@ -46,12 +47,6 @@ namespace sip {
         return pj_str(const_cast<char *>(str.c_str()));
     }
 
-    pj_status_t MediaPort_getFrameRawCallback(pjmedia_port *port, pjmedia_frame *frame);
-
-    pj_status_t MediaPort_putFrameRawCallback(pjmedia_port *port, pjmedia_frame *frame);
-
-    void PjsuaCommunicator_onCallMediaState(pjsua_call_id call_id);
-
     class PjsuaCommunicator : public ICommunicator, boost::noncopyable {
     public:
         PjsuaCommunicator();
@@ -68,12 +63,27 @@ namespace sip {
 
         std::function<void(std::string)> onStateChange;
 
+        void onCallMediaState(pjsua_call_id call_id);
+
+        void onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata);
+
+        void onDtmfDigit(pjsua_call_id callId, int digit);
+
+        void onCallState(pjsua_call_id call_id, pjsip_event *e);
+
+        pj_status_t mediaPortGetFrame(pjmedia_port *port, pjmedia_frame *frame);
+
+        pj_status_t mediaPortPutFrame(pjmedia_port *port, pjmedia_frame *frame);
+
+        pj_status_t _mediaPortOnDestroy(pjmedia_port *port);
+
     private:
         log4cpp::Category &logger;
         log4cpp::Category &callbackLogger;
 
-
-        pjmedia_port *mediaPort = nullptr;
+        pjmedia_port mediaPort;
+        int mediaPortSlot = INT_MIN;
+        bool eof = false;
 
         pj_pool_t *pool = nullptr;
 
@@ -81,28 +91,14 @@ namespace sip {
 
         std::mutex inBuffAccessMutex;
 
-        // todo make it completely stateless
-        pjmedia_port *createMediaPort();
+        bool available = true;
+
+        void createMediaPort();
 
         void registerAccount(std::string host,
                              std::string user,
                              std::string password);
 
-        pj_status_t mediaPortGetFrame(pjmedia_frame *frame);
-
-        void mediaPortPutFrame(pj_int16_t *samples, pj_size_t count);
-
-        /**
-        * For PJMEDIA implementation reasons, these callbacks have to be functions, not methods.
-        * That is the reason to use 'friend'.
-        */
-        friend pj_status_t MediaPort_getFrameRawCallback(pjmedia_port *port,
-                                                         pjmedia_frame *frame);
-
-        friend pj_status_t MediaPort_putFrameRawCallback(pjmedia_port *port,
-                                                         pjmedia_frame *frame);
-
-        friend void PjsuaCommunicator_onCallMediaState(pjsua_call_id call_id);
     };
 
 }