Refactor PJSUA callbacks.

This commit is contained in:
Michał Słomkowski 2015-11-06 02:23:48 +01:00
parent c4e0ce5f14
commit 3267329a63
3 changed files with 147 additions and 115 deletions

6
.gitignore vendored
View File

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

View File

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

View File

@ -13,6 +13,7 @@
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <mutex> #include <mutex>
#include <climits>
namespace sip { namespace sip {
@ -46,12 +47,6 @@ namespace sip {
return pj_str(const_cast<char *>(str.c_str())); 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 { class PjsuaCommunicator : public ICommunicator, boost::noncopyable {
public: public:
PjsuaCommunicator(); PjsuaCommunicator();
@ -68,12 +63,27 @@ namespace sip {
std::function<void(std::string)> onStateChange; 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: private:
log4cpp::Category &logger; log4cpp::Category &logger;
log4cpp::Category &callbackLogger; log4cpp::Category &callbackLogger;
pjmedia_port mediaPort;
pjmedia_port *mediaPort = nullptr; int mediaPortSlot = INT_MIN;
bool eof = false;
pj_pool_t *pool = nullptr; pj_pool_t *pool = nullptr;
@ -81,28 +91,14 @@ namespace sip {
std::mutex inBuffAccessMutex; std::mutex inBuffAccessMutex;
// todo make it completely stateless bool available = true;
pjmedia_port *createMediaPort();
void createMediaPort();
void registerAccount(std::string host, void registerAccount(std::string host,
std::string user, std::string user,
std::string password); 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);
}; };
} }