diff --git a/CMakeLists.txt b/CMakeLists.txt index e38e2f4..5b275aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,13 +8,16 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(OPUS "opus") pkg_check_modules(SNDFILE "sndfile") +pkg_check_modules(PJSIP "libpjproject") include_directories(${OPUS_INCLUDE_DIRS}) include_directories(libmumble) +include_directories(${PJSIP_INCLUDE_DIRS}) link_directories(libmumble) -set(SOURCE_FILES main.cpp) +set(SOURCE_FILES main.cpp PjsuaCommunicator.cpp PjsuaMediaPort.cpp) add_executable(mumsi ${SOURCE_FILES}) target_link_libraries(mumsi ${OPUS_LIBRARIES}) target_link_libraries(mumsi ${SNDFILE_LIBRARIES}) +target_link_libraries(mumsi ${PJSIP_LIBRARIES}) target_link_libraries(mumsi mumble) diff --git a/PjsuaCommunicator.cpp b/PjsuaCommunicator.cpp new file mode 100644 index 0000000..495264f --- /dev/null +++ b/PjsuaCommunicator.cpp @@ -0,0 +1,149 @@ +#include "PjsuaCommunicator.hpp" + + +#include +#include + +#include + +//todo wywalić +#define THIS_FILE "mumsi" + +using namespace std; + + +/** + * This is global, because there's no way to pass it's value to onCallMediaState callback. + */ +static int mediaPortSlot; + + +static void 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) { + 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) { + pjsua_call_info ci; + + PJ_UNUSED_ARG(acc_id); + PJ_UNUSED_ARG(rdata); + + pjsua_call_get_info(call_id, &ci); + + PJ_LOG(3, (THIS_FILE, "Incoming call from %.*s!!", + (int) ci.remote_info.slen, + ci.remote_info.ptr)); + + /* Automatically answer incoming calls with 200/OK */ + pjsua_call_answer(call_id, 200, NULL, NULL); +} + +static void onCallState(pjsua_call_id call_id, + pjsip_event *e) { + pjsua_call_info ci; + + PJ_UNUSED_ARG(e); + + pjsua_call_get_info(call_id, &ci); + PJ_LOG(3, (THIS_FILE, "Call %d state=%.*s", call_id, + (int) ci.state_text.slen, + ci.state_text.ptr)); +} + + +pjsua::PjsuaCommunicator::PjsuaCommunicator(std::string host, + std::string user, + std::string password, + PjsuaMediaPort &mediaPort) : mediaPort(mediaPort) { + + pj_status_t status; + + status = pjsua_create(); + if (status != PJ_SUCCESS) { + throw pjsua::Exception("Error in pjsua_create()", status); + } + + /* Init pjsua */ + pjsua_config generalConfig; + pjsua_config_default(&generalConfig); + + using namespace std::placeholders; + + generalConfig.cb.on_incoming_call = &onIncomingCall; + generalConfig.cb.on_call_media_state = &onCallMediaState; + generalConfig.cb.on_call_state = &onCallState; + + //todo zrobić coś z logami + pjsua_logging_config logConfig; + pjsua_logging_config_default(&logConfig); + + logConfig.console_level = 4; + + status = pjsua_init(&generalConfig, &logConfig, NULL); + if (status != PJ_SUCCESS) { + throw pjsua::Exception("Error in pjsua_init()", status); + } + + /* Add UDP transport. */ + pjsua_transport_config transportConfig; + pjsua_transport_config_default(&transportConfig); + + transportConfig.port = pjsua::SIP_PORT; + + status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transportConfig, NULL); + if (status != PJ_SUCCESS) { + throw pjsua::Exception("Error creating transport", status); + } + + pjsua_set_null_snd_dev(); + + pj_caching_pool cachingPool; + pj_caching_pool_init(&cachingPool, &pj_pool_factory_default_policy, 0); + pj_pool_t *pool = pj_pool_create(&cachingPool.factory, "wav", 4096, 4096, nullptr); + + pjsua_conf_add_port(pool, mediaPort.create_pjmedia_port(), &mediaPortSlot); + + /* Initialization is done, now start pjsua */ + status = pjsua_start(); + if (status != PJ_SUCCESS) { + throw pjsua::Exception("Error starting pjsua", status); + } + + /* Register to SIP server by creating SIP account. */ + pjsua_acc_config accConfig; + + pjsua_acc_config_default(&accConfig); + + accConfig.id = toPjString(string("sip:") + user + "@" + host); + accConfig.reg_uri = toPjString(string("sip:") + host); + + accConfig.cred_count = 1; + accConfig.cred_info[0].realm = toPjString(host); + accConfig.cred_info[0].scheme = toPjString("digest"); + 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); + + pjsua_acc_id acc_id; + status = pjsua_acc_add(&accConfig, PJ_TRUE, &acc_id); + if (status != PJ_SUCCESS) { + throw pjsua::Exception("Error adding account", status); + } +} + +pjsua::PjsuaCommunicator::~PjsuaCommunicator() { + pjsua_destroy(); +} + +void pjsua::PjsuaCommunicator::loop() { + +} diff --git a/PjsuaCommunicator.hpp b/PjsuaCommunicator.hpp new file mode 100644 index 0000000..5b3b283 --- /dev/null +++ b/PjsuaCommunicator.hpp @@ -0,0 +1,32 @@ +#ifndef MUMSI_PJSUACOMMUNICATOR_HPP +#define MUMSI_PJSUACOMMUNICATOR_HPP + +#include "PjsuaMediaPort.hpp" + +#include + +#include +#include + +namespace pjsua { + + constexpr int SIP_PORT = 5060; + + class PjsuaCommunicator { + public: + PjsuaCommunicator(std::string host, + std::string user, + std::string password, + PjsuaMediaPort &mediaPort); + + ~PjsuaCommunicator(); + + void loop(); + + private: + PjsuaMediaPort &mediaPort; + }; + +} + +#endif //MUMSI_PJSUACOMMUNICATOR_HPP diff --git a/PjsuaMediaPort.cpp b/PjsuaMediaPort.cpp new file mode 100644 index 0000000..3a32500 --- /dev/null +++ b/PjsuaMediaPort.cpp @@ -0,0 +1,64 @@ +#include "PjsuaMediaPort.hpp" + +using namespace std; +using namespace pjsua; + + +static pj_status_t getFrame(pjmedia_port *port, + pjmedia_frame *frame) { + PjsuaMediaPort *mediaPort = static_cast(port->port_data.pdata); + pj_int16_t *samples = static_cast(frame->buf); + pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info); + + //todo code here + for (int i = 0; i < count; ++i) { + samples[i] = 10000.8 * (i % 10); + } + + frame->type = PJMEDIA_FRAME_TYPE_AUDIO; + + return PJ_SUCCESS; +} + +static pj_status_t putFrame(pjmedia_port *port, + pjmedia_frame *frame) { + PjsuaMediaPort *mediaPort = static_cast(port->port_data.pdata); + pj_int16_t *samples = static_cast(frame->buf); + pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info); + + //todo code here + + return PJ_SUCCESS; +} + +pjmedia_port *pjsua::PjsuaMediaPort::create_pjmedia_port() { + + _pjmedia_port = new pjmedia_port(); + + pj_str_t name = toPjString("PjsuaMediaPort"); + + pj_status_t status = pjmedia_port_info_init(&(_pjmedia_port->info), + &name, + PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'), + SAMPLING_RATE, + 1, + 16, + SAMPLING_RATE * 20 / 1000); // todo recalculate to match mumble specs + + if (status != PJ_SUCCESS) { + throw pjsua::Exception("Error while calling pjmedia_port_info_init().", status); + } + + _pjmedia_port->get_frame = &getFrame; + _pjmedia_port->put_frame = &putFrame; + + _pjmedia_port->port_data.pdata = this; + + return _pjmedia_port; +} + +pjsua::PjsuaMediaPort::~PjsuaMediaPort() { + if (this->_pjmedia_port != nullptr) { + delete _pjmedia_port; + } +} \ No newline at end of file diff --git a/PjsuaMediaPort.hpp b/PjsuaMediaPort.hpp new file mode 100644 index 0000000..7f02e14 --- /dev/null +++ b/PjsuaMediaPort.hpp @@ -0,0 +1,39 @@ +#ifndef MUMSI_PJSUAMEDIAPORT_HPP +#define MUMSI_PJSUAMEDIAPORT_HPP + +#include + +#include +#include + +namespace pjsua { + + constexpr int SAMPLING_RATE = 8000; + + inline pj_str_t toPjString(std::string str) { + return pj_str(const_cast(str.c_str())); + } + + class Exception : public std::runtime_error { + public: + Exception(const char *title, pj_status_t status) : std::runtime_error(title) { + //todo status code + } + }; + + class PjsuaMediaPort { + public: + + PjsuaMediaPort() : _pjmedia_port(nullptr) { } + + ~PjsuaMediaPort(); + + + pjmedia_port *create_pjmedia_port(); + + private: + pjmedia_port *_pjmedia_port; + }; +} + +#endif //MUMSI_PJSUAMEDIAPORT_HPP diff --git a/sim.c b/sim.c index 2350e72..0e3bad2 100644 --- a/sim.c +++ b/sim.c @@ -104,8 +104,8 @@ static pj_status_t create_sine_port(pj_pool_t *pool, pjmedia_port_info_init(&port->info, &name, PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'), sampling_rate, - channel_count, - 16, sampling_rate * 20 / 1000 * channel_count); + 1, + 16, sampling_rate * 20 / 1000 * 1); /* Set the function to feed frame */ port->get_frame = &sine_get_frame; @@ -265,12 +265,6 @@ int main(int argc, char *argv[]) { if (status != PJ_SUCCESS) error_exit("Error adding account", status); } - /* If URL is specified, make call to the URL. */ - if (argc > 1) { - pj_str_t uri = pj_str(argv[1]); - status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, NULL); - if (status != PJ_SUCCESS) error_exit("Error making call", status); - } /* Wait until user press "q" to quit. */ for (; ;) {