PjsuaCommunicator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #include "PjsuaCommunicator.hpp"
  2. #include <pjlib.h>
  3. #include <pjsua-lib/pjsua.h>
  4. #include <boost/algorithm/string.hpp>
  5. #include <boost/format.hpp>
  6. using namespace std;
  7. /**
  8. * These are global, because there's no way to pass it's value to onCallMediaState callback.
  9. */
  10. //static int mediaPortSlot = -1;
  11. //static sip::PjsuaCommunicator *pjsuaCommunicator = nullptr;
  12. static log4cpp::Category &pjLogger = log4cpp::Category::getInstance("PjSip");
  13. void sip::PjsuaCommunicator::onCallMediaState(pjsua_call_id call_id) {
  14. pjsua_call_info ci;
  15. pjsua_call_get_info(call_id, &ci);
  16. if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
  17. pjsua_conf_connect(ci.conf_slot, mediaPortSlot);
  18. pjsua_conf_connect(mediaPortSlot, ci.conf_slot);
  19. }
  20. }
  21. void sip::PjsuaCommunicator::onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata) {
  22. pjsua_call_info ci;
  23. PJ_UNUSED_ARG(rdata);
  24. pjsua_call_get_info(call_id, &ci);
  25. pjsua_call_set_user_data(call_id, this);
  26. logger.info("Incoming call from %s.", ci.remote_info.ptr);
  27. if (this->available) {
  28. available = false;
  29. pjsua_call_set_user_data(call_id, this);
  30. pjsua_call_answer(call_id, 200, nullptr, nullptr);
  31. } else {
  32. // 486 Busy Here - Callee is Busy
  33. pjsua_call_answer(call_id, 486, nullptr, nullptr);
  34. }
  35. }
  36. void sip::PjsuaCommunicator::onDtmfDigit(pjsua_call_id callId, int digit) {
  37. logger.notice("DTMF digit '%c' (call %d).", digit, callId);
  38. }
  39. void sip::PjsuaCommunicator::onCallState(pjsua_call_id call_id, pjsip_event *e) {
  40. pjsua_call_info ci;
  41. PJ_UNUSED_ARG(e);
  42. pjsua_call_get_info(call_id, &ci);
  43. logger.info("Call %d state=%s.", call_id, ci.state_text.ptr);
  44. string address = string(ci.remote_info.ptr);
  45. boost::replace_all(address, "<", "");
  46. boost::replace_all(address, ">", "");
  47. if (ci.state == PJSIP_INV_STATE_CONFIRMED) {
  48. auto msgText = "Start call from " + address + ".";
  49. logger.notice(msgText);
  50. onStateChange(msgText);
  51. } else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
  52. auto msgText = "End call from " + address + ".";
  53. logger.notice(msgText);
  54. onStateChange(msgText);
  55. available = true;
  56. }
  57. }
  58. static void callback_onCallMediaState(pjsua_call_id callId) {
  59. auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_call_get_user_data(callId));
  60. communicator->onCallMediaState(callId);
  61. }
  62. static void callback_onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata) {
  63. auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_acc_get_user_data(acc_id));
  64. communicator->onIncomingCall(acc_id, call_id, rdata);
  65. }
  66. static void callback_onDtmfDigit(pjsua_call_id callId, int digit) {
  67. auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_call_get_user_data(callId));
  68. communicator->onDtmfDigit(callId, digit);
  69. }
  70. static void callback_onCallState(pjsua_call_id call_id, pjsip_event *e) {
  71. auto *communicator = static_cast<sip::PjsuaCommunicator *>(pjsua_call_get_user_data(call_id));
  72. communicator->onCallState(call_id, e);
  73. }
  74. static pj_status_t callback_getFrame(pjmedia_port *port, pjmedia_frame *frame) {
  75. auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
  76. return communicator->mediaPortGetFrame(port, frame);
  77. }
  78. static pj_status_t callback_putFrame(pjmedia_port *port, pjmedia_frame *frame) {
  79. auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
  80. return communicator->mediaPortPutFrame(port, frame);
  81. }
  82. static pj_status_t callback_onDestroy(pjmedia_port *port) {
  83. auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
  84. return communicator->_mediaPortOnDestroy(port);
  85. }
  86. static void pjLogToLog4CppBridgeFunction(int level, const char *data, int len) {
  87. using namespace log4cpp;
  88. std::map<int, Priority::Value> prioritiesMap = {
  89. {1, Priority::ERROR},
  90. {2, Priority::WARN},
  91. {3, Priority::NOTICE},
  92. {4, Priority::INFO},
  93. {5, Priority::DEBUG},
  94. {6, Priority::DEBUG}
  95. };
  96. string message(data);
  97. message = message.substr(0, message.size() - 1); // remove newline
  98. pjLogger << prioritiesMap.at(level) << message;
  99. }
  100. sip::PjsuaCommunicator::PjsuaCommunicator()
  101. : logger(log4cpp::Category::getInstance("SipCommunicator")),
  102. callbackLogger(log4cpp::Category::getInstance("SipCommunicatorCallback")) {
  103. pj_status_t status;
  104. status = pjsua_create();
  105. if (status != PJ_SUCCESS) {
  106. throw sip::Exception("Error in pjsua_create()", status);
  107. }
  108. pj_log_set_log_func(pjLogToLog4CppBridgeFunction);
  109. pjsua_config generalConfig;
  110. pjsua_config_default(&generalConfig);
  111. string userAgent = "Mumsi Mumble-SIP Bridge";
  112. generalConfig.user_agent = toPjString(userAgent);
  113. generalConfig.max_calls = 1;
  114. generalConfig.cb.on_incoming_call = &callback_onIncomingCall;
  115. generalConfig.cb.on_dtmf_digit = &callback_onDtmfDigit;
  116. generalConfig.cb.on_call_media_state = &callback_onCallMediaState;
  117. generalConfig.cb.on_call_state = &callback_onCallState;
  118. pjsua_logging_config logConfig;
  119. pjsua_logging_config_default(&logConfig);
  120. logConfig.cb = pjLogToLog4CppBridgeFunction;
  121. logConfig.console_level = 5;
  122. pjsua_media_config mediaConfig;
  123. pjsua_media_config_default(&mediaConfig);
  124. mediaConfig.no_vad = 1;
  125. status = pjsua_init(&generalConfig, &logConfig, &mediaConfig);
  126. if (status != PJ_SUCCESS) {
  127. throw sip::Exception("error in pjsua_init", status);
  128. }
  129. pj_caching_pool cachingPool;
  130. pj_caching_pool_init(&cachingPool, &pj_pool_factory_default_policy, 0);
  131. pool = pj_pool_create(&cachingPool.factory, "wav", 32768, 8192, nullptr);
  132. // todo calculate sizes
  133. status = pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff);
  134. if (status != PJ_SUCCESS) {
  135. throw sip::Exception("error when creating circular buffer", status);
  136. }
  137. logger.notice("Active ports: %d.", pjsua_conf_get_active_ports());
  138. createMediaPort();
  139. status = pjsua_conf_add_port(pool, &mediaPort, &mediaPortSlot);
  140. if (status != PJ_SUCCESS) {
  141. throw sip::Exception("error when calling pjsua_conf_add_port", status);
  142. }
  143. }
  144. void sip::PjsuaCommunicator::connect(
  145. std::string host,
  146. std::string user,
  147. std::string password,
  148. unsigned int port) {
  149. pj_status_t status;
  150. pjsua_transport_config transportConfig;
  151. pjsua_transport_config_default(&transportConfig);
  152. transportConfig.port = port;
  153. status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transportConfig, NULL);
  154. if (status != PJ_SUCCESS) {
  155. throw sip::Exception("error creating transport", status);
  156. }
  157. status = pjsua_start();
  158. if (status != PJ_SUCCESS) {
  159. throw sip::Exception("error starting PJSUA", status);
  160. }
  161. status = pjsua_set_null_snd_dev();
  162. if (status != PJ_SUCCESS) {
  163. throw sip::Exception("error in pjsua_set_null_std_dev()", status);
  164. }
  165. registerAccount(host, user, password);
  166. }
  167. sip::PjsuaCommunicator::~PjsuaCommunicator() {
  168. pjsua_destroy();
  169. }
  170. void sip::PjsuaCommunicator::createMediaPort() {
  171. eof = false;
  172. string name = "PjsuamediaPort";
  173. auto pjName = toPjString(name);
  174. pj_status_t status = pjmedia_port_info_init(&(mediaPort.info),
  175. &pjName,
  176. PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'),
  177. SAMPLING_RATE,
  178. 1,
  179. 16,
  180. SAMPLING_RATE * 20 /
  181. 1000); // todo recalculate to match mumble specs
  182. if (status != PJ_SUCCESS) {
  183. throw sip::Exception("error while calling pjmedia_port_info_init().", status);
  184. }
  185. mediaPort.port_data.pdata = this;
  186. mediaPort.get_frame = &callback_getFrame;
  187. mediaPort.put_frame = &callback_putFrame;
  188. mediaPort.on_destroy = &callback_onDestroy;
  189. }
  190. pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_port *port, pjmedia_frame *frame) {
  191. std::unique_lock<std::mutex> lock(inBuffAccessMutex);
  192. if (this->eof) {
  193. return PJ_EEOF;
  194. }
  195. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  196. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  197. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&(port->info));
  198. pj_size_t availableSamples = pjmedia_circ_buf_get_len(inputBuff);
  199. const int samplesToRead = std::min(count, availableSamples);
  200. callbackLogger.debug("Pulling %d samples from in-buff.", samplesToRead);
  201. pjmedia_circ_buf_read(inputBuff, samples, samplesToRead);
  202. if (availableSamples < count) {
  203. callbackLogger.debug("Requested %d samples, available %d, filling remaining with zeros.", count,
  204. availableSamples);
  205. for (int i = samplesToRead; i < count; ++i) {
  206. samples[i] = 0;
  207. }
  208. }
  209. return PJ_SUCCESS;
  210. }
  211. pj_status_t sip::PjsuaCommunicator::mediaPortPutFrame(pjmedia_port *port, pjmedia_frame *frame) {
  212. if (this->eof) {
  213. return PJ_EEOF;
  214. }
  215. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  216. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info);
  217. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  218. if (count > 0) {
  219. callbackLogger.debug("Calling onIncomingPcmSamples with %d samples.", count);
  220. onIncomingPcmSamples(samples, count);
  221. }
  222. return PJ_SUCCESS;
  223. }
  224. pj_status_t sip::PjsuaCommunicator::_mediaPortOnDestroy(pjmedia_port *port) {
  225. eof = true;
  226. return PJ_SUCCESS;
  227. }
  228. void sip::PjsuaCommunicator::registerAccount(string host, string user, string password) {
  229. pjsua_acc_config accConfig;
  230. pjsua_acc_config_default(&accConfig);
  231. string uri = string("sip:") + user + "@" + host;
  232. string regUri = "sip:" + host;
  233. string scheme = "digest";
  234. pj_status_t status;
  235. status = pjsua_verify_sip_url(uri.c_str());
  236. if (status != PJ_SUCCESS) {
  237. throw sip::Exception("invalid URI format", status);
  238. }
  239. logger.info("Registering account for URI: %s.", uri.c_str());
  240. accConfig.id = toPjString(uri);
  241. accConfig.reg_uri = toPjString(regUri);
  242. accConfig.cred_count = 1;
  243. accConfig.cred_info[0].realm = toPjString(host);
  244. accConfig.cred_info[0].scheme = toPjString(scheme);
  245. accConfig.cred_info[0].username = toPjString(user);
  246. accConfig.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
  247. accConfig.cred_info[0].data = toPjString(password);
  248. accConfig.user_data = this;
  249. pjsua_acc_id acc_id;
  250. status = pjsua_acc_add(&accConfig, PJ_TRUE, &acc_id);
  251. if (status != PJ_SUCCESS) {
  252. throw sip::Exception("failed to register account", status);
  253. }
  254. }
  255. void sip::PjsuaCommunicator::sendPcmSamples(int16_t *samples, unsigned int length) {
  256. std::unique_lock<std::mutex> lock(inBuffAccessMutex);
  257. callbackLogger.debug("Pushing %d samples to in-buff.", length);
  258. pjmedia_circ_buf_write(inputBuff, samples, length);
  259. }