PjsuaCommunicator.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. namespace sip {
  8. using namespace log4cpp;
  9. class _LogWriter : public pj::LogWriter {
  10. public:
  11. _LogWriter(Category &logger)
  12. : logger(logger) { }
  13. virtual void write(const pj::LogEntry &entry) override {
  14. auto message = entry.msg.substr(0, entry.msg.size() - 1); // remove newline
  15. logger << prioritiesMap.at(entry.level) << message;
  16. }
  17. private:
  18. log4cpp::Category &logger;
  19. std::map<int, Priority::Value> prioritiesMap = {
  20. {1, Priority::ERROR},
  21. {2, Priority::WARN},
  22. {3, Priority::NOTICE},
  23. {4, Priority::INFO},
  24. {5, Priority::DEBUG},
  25. {6, Priority::DEBUG}
  26. };
  27. };
  28. class _MumlibAudioMedia : public pj::AudioMedia {
  29. public:
  30. _MumlibAudioMedia(sip::PjsuaCommunicator &comm)
  31. : communicator(comm) {
  32. createMediaPort();
  33. registerMediaPort(&mediaPort);
  34. }
  35. ~_MumlibAudioMedia() {
  36. unregisterMediaPort();
  37. }
  38. private:
  39. pjmedia_port mediaPort;
  40. sip::PjsuaCommunicator &communicator;
  41. static pj_status_t callback_getFrame(pjmedia_port *port, pjmedia_frame *frame) {
  42. auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
  43. return communicator->mediaPortGetFrame(port, frame);
  44. }
  45. static pj_status_t callback_putFrame(pjmedia_port *port, pjmedia_frame *frame) {
  46. auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
  47. return communicator->mediaPortPutFrame(port, frame);
  48. }
  49. void createMediaPort() {
  50. auto name = pj_str((char *) "MumsiMediaPort");
  51. pj_status_t status = pjmedia_port_info_init(&(mediaPort.info),
  52. &name,
  53. PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'),
  54. SAMPLING_RATE,
  55. 1,
  56. 16,
  57. SAMPLING_RATE * 20 /
  58. 1000); // todo recalculate to match mumble specs
  59. if (status != PJ_SUCCESS) {
  60. throw sip::Exception("error while calling pjmedia_port_info_init()", status);
  61. }
  62. mediaPort.port_data.pdata = &communicator;
  63. mediaPort.get_frame = &callback_getFrame;
  64. mediaPort.put_frame = &callback_putFrame;
  65. }
  66. };
  67. class _Call : public pj::Call {
  68. public:
  69. _Call(sip::PjsuaCommunicator &comm, pj::Account &acc, int call_id = PJSUA_INVALID_ID)
  70. : pj::Call(acc, call_id),
  71. communicator(comm),
  72. account(acc) { }
  73. virtual void onCallState(pj::OnCallStateParam &prm) override;
  74. virtual void onCallMediaState(pj::OnCallMediaStateParam &prm) override;
  75. virtual void onDtmfDigit(pj::OnDtmfDigitParam &prm) override;
  76. private:
  77. sip::PjsuaCommunicator &communicator;
  78. pj::Account &account;
  79. };
  80. class _Account : public pj::Account {
  81. public:
  82. _Account(sip::PjsuaCommunicator &comm)
  83. : communicator(comm) { }
  84. virtual void onRegState(pj::OnRegStateParam &prm) override;
  85. virtual void onIncomingCall(pj::OnIncomingCallParam &iprm) override;
  86. private:
  87. sip::PjsuaCommunicator &communicator;
  88. bool available = true;
  89. friend class _Call;
  90. };
  91. void _Call::onCallState(pj::OnCallStateParam &prm) {
  92. auto ci = getInfo();
  93. communicator.logger.info("Call %d state=%s.", ci.id, ci.stateText.c_str());
  94. string address = ci.remoteUri;
  95. boost::replace_all(address, "<", "");
  96. boost::replace_all(address, ">", "");
  97. if (ci.state == PJSIP_INV_STATE_CONFIRMED) {
  98. auto msgText = "Incoming call from " + address + ".";
  99. communicator.logger.notice(msgText);
  100. communicator.onStateChange(msgText);
  101. } else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
  102. auto &acc = dynamic_cast<_Account &>(account);
  103. if (not acc.available) {
  104. auto msgText = "Call from " + address + " finished.";
  105. communicator.logger.notice(msgText);
  106. communicator.onStateChange(msgText);
  107. acc.available = true;
  108. }
  109. delete this;
  110. }
  111. }
  112. void _Call::onCallMediaState(pj::OnCallMediaStateParam &prm) {
  113. auto ci = getInfo();
  114. if (ci.media.size() != 1) {
  115. throw sip::Exception("ci.media.size is not 1");
  116. }
  117. if (ci.media[0].status == PJSUA_CALL_MEDIA_ACTIVE) {
  118. auto *aud_med = static_cast<pj::AudioMedia *>(getMedia(0));
  119. communicator.media->startTransmit(*aud_med);
  120. aud_med->startTransmit(*communicator.media);
  121. } else if (ci.media[0].status == PJSUA_CALL_MEDIA_NONE) {
  122. dynamic_cast<_Account &>(account).available = true;
  123. }
  124. }
  125. void _Call::onDtmfDigit(pj::OnDtmfDigitParam &prm) {
  126. communicator.logger.notice("DTMF digit '%s' (call %d).", prm.digit.c_str(), getId());
  127. }
  128. void _Account::onRegState(pj::OnRegStateParam &prm) {
  129. pj::AccountInfo ai = getInfo();
  130. communicator.logger << log4cpp::Priority::INFO
  131. << (ai.regIsActive ? "Register:" : "Unregister:") << " code=" << prm.code;
  132. }
  133. void _Account::onIncomingCall(pj::OnIncomingCallParam &iprm) {
  134. auto *call = new _Call(communicator, *this, iprm.callId);
  135. string uri = call->getInfo().remoteUri;
  136. communicator.logger.info("Incoming call from %s.", uri.c_str());
  137. pj::CallOpParam param;
  138. if (communicator.uriValidator.validateUri(uri)) {
  139. if (available) {
  140. param.statusCode = PJSIP_SC_OK;
  141. available = false;
  142. } else {
  143. param.statusCode = PJSIP_SC_BUSY_EVERYWHERE;
  144. }
  145. call->answer(param);
  146. } else {
  147. communicator.logger.warn("Refusing call from %s.", uri.c_str());
  148. param.statusCode = PJSIP_SC_SERVICE_UNAVAILABLE;
  149. call->hangup(param);
  150. }
  151. }
  152. }
  153. sip::PjsuaCommunicator::PjsuaCommunicator(IncomingConnectionValidator &validator)
  154. : logger(log4cpp::Category::getInstance("SipCommunicator")),
  155. pjsuaLogger(log4cpp::Category::getInstance("Pjsua")),
  156. uriValidator(validator) {
  157. logWriter.reset(new sip::_LogWriter(pjsuaLogger));
  158. endpoint.libCreate();
  159. pj::EpConfig endpointConfig;
  160. endpointConfig.uaConfig.userAgent = "Mumsi Mumble-SIP gateway";
  161. endpointConfig.uaConfig.maxCalls = 1;
  162. endpointConfig.logConfig.writer = logWriter.get();
  163. endpointConfig.logConfig.level = 5;
  164. endpointConfig.medConfig.noVad = true;
  165. endpoint.libInit(endpointConfig);
  166. pj_status_t status;
  167. pj_caching_pool cachingPool;
  168. pj_caching_pool_init(&cachingPool, &pj_pool_factory_default_policy, 0);
  169. pool = pj_pool_create(&cachingPool.factory, "media", 32768, 8192, nullptr);
  170. if (!pool) {
  171. throw sip::Exception("error when creating memory pool", status);
  172. }
  173. // todo calculate sizes
  174. status = pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff);
  175. if (status != PJ_SUCCESS) {
  176. throw sip::Exception("error when creating circular buffer", status);
  177. }
  178. media.reset(new _MumlibAudioMedia(*this));
  179. }
  180. void sip::PjsuaCommunicator::connect(
  181. std::string host,
  182. std::string user,
  183. std::string password,
  184. unsigned int port) {
  185. pj::TransportConfig transportConfig;
  186. transportConfig.port = port;
  187. endpoint.transportCreate(PJSIP_TRANSPORT_UDP, transportConfig); // todo try catch
  188. endpoint.libStart();
  189. pj_status_t status = pjsua_set_null_snd_dev();
  190. if (status != PJ_SUCCESS) {
  191. throw sip::Exception("error in pjsua_set_null_std_dev()", status);
  192. }
  193. registerAccount(host, user, password);
  194. }
  195. sip::PjsuaCommunicator::~PjsuaCommunicator() {
  196. endpoint.libDestroy();
  197. }
  198. pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_port *port, pjmedia_frame *frame) {
  199. std::unique_lock<std::mutex> lock(inBuffAccessMutex);
  200. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  201. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  202. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&(port->info));
  203. pj_size_t availableSamples = pjmedia_circ_buf_get_len(inputBuff);
  204. const int samplesToRead = std::min(count, availableSamples);
  205. pjsuaLogger.debug("Pulling %d samples from in-buff.", samplesToRead);
  206. pjmedia_circ_buf_read(inputBuff, samples, samplesToRead);
  207. if (availableSamples < count) {
  208. pjsuaLogger.debug("Requested %d samples, available %d, filling remaining with zeros.", count,
  209. availableSamples);
  210. for (int i = samplesToRead; i < count; ++i) {
  211. samples[i] = 0;
  212. }
  213. }
  214. return PJ_SUCCESS;
  215. }
  216. pj_status_t sip::PjsuaCommunicator::mediaPortPutFrame(pjmedia_port *port, pjmedia_frame *frame) {
  217. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  218. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info);
  219. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  220. if (count > 0) {
  221. pjsuaLogger.debug("Calling onIncomingPcmSamples with %d samples.", count);
  222. onIncomingPcmSamples(samples, count);
  223. }
  224. return PJ_SUCCESS;
  225. }
  226. void sip::PjsuaCommunicator::registerAccount(string host, string user, string password) {
  227. string uri = "sip:" + user + "@" + host;
  228. pj::AccountConfig accountConfig;
  229. accountConfig.idUri = uri;
  230. accountConfig.regConfig.registrarUri = "sip:" + host;
  231. pj::AuthCredInfo cred("digest", "*", user, 0, password);
  232. accountConfig.sipConfig.authCreds.push_back(cred);
  233. logger.info("Registering account for URI: %s.", uri.c_str());
  234. account.reset(new _Account(*this));
  235. account->create(accountConfig);
  236. }
  237. void sip::PjsuaCommunicator::sendPcmSamples(int16_t *samples, unsigned int length) {
  238. std::unique_lock<std::mutex> lock(inBuffAccessMutex);
  239. pjsuaLogger.debug("Pushing %d samples to in-buff.", length);
  240. pjmedia_circ_buf_write(inputBuff, samples, length);
  241. }