PjsuaCommunicator.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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. #include "main.hpp"
  7. using namespace std;
  8. namespace sip {
  9. using namespace log4cpp;
  10. class _LogWriter : public pj::LogWriter {
  11. public:
  12. _LogWriter(Category &logger)
  13. : logger(logger) { }
  14. virtual void write(const pj::LogEntry &entry) override {
  15. auto message = entry.msg.substr(0, entry.msg.size() - 1); // remove newline
  16. logger << prioritiesMap.at(entry.level) << message;
  17. }
  18. private:
  19. log4cpp::Category &logger;
  20. std::map<int, Priority::Value> prioritiesMap = {
  21. {1, Priority::ERROR},
  22. {2, Priority::WARN},
  23. {3, Priority::NOTICE},
  24. {4, Priority::INFO},
  25. {5, Priority::DEBUG},
  26. {6, Priority::DEBUG}
  27. };
  28. };
  29. class _MumlibAudioMedia : public pj::AudioMedia {
  30. public:
  31. _MumlibAudioMedia(int call_id, sip::PjsuaCommunicator &comm, int frameTimeLength)
  32. : communicator(comm) {
  33. createMediaPort(call_id, frameTimeLength);
  34. registerMediaPort(&mediaPort);
  35. }
  36. ~_MumlibAudioMedia() {
  37. unregisterMediaPort();
  38. }
  39. private:
  40. pjmedia_port mediaPort;
  41. sip::PjsuaCommunicator &communicator;
  42. static pj_status_t callback_getFrame(pjmedia_port *port, pjmedia_frame *frame) {
  43. auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
  44. return communicator->mediaPortGetFrame(port, frame);
  45. }
  46. static pj_status_t callback_putFrame(pjmedia_port *port, pjmedia_frame *frame) {
  47. auto *communicator = static_cast<sip::PjsuaCommunicator *>(port->port_data.pdata);
  48. return communicator->mediaPortPutFrame(port, frame);
  49. }
  50. void createMediaPort(int call_id, int frameTimeLength) {
  51. auto name = pj_str((char *) "MumsiMediaPort");
  52. if (frameTimeLength != 10
  53. and frameTimeLength != 20
  54. and frameTimeLength != 40
  55. and frameTimeLength != 60) {
  56. throw sip::Exception(
  57. (boost::format("valid frame time length value: %d. valid values are: 10, 20, 40, 60") %
  58. frameTimeLength).str());
  59. }
  60. pj_status_t status = pjmedia_port_info_init(&(mediaPort.info),
  61. &name,
  62. PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'),
  63. SAMPLING_RATE,
  64. 1,
  65. 16,
  66. SAMPLING_RATE * frameTimeLength / 1000);
  67. if (status != PJ_SUCCESS) {
  68. throw sip::Exception("error while calling pjmedia_port_info_init()", status);
  69. }
  70. mediaPort.port_data.pdata = &communicator;
  71. // track call id in port_data
  72. mediaPort.port_data.ldata = (long) call_id;
  73. mediaPort.get_frame = &callback_getFrame;
  74. mediaPort.put_frame = &callback_putFrame;
  75. }
  76. };
  77. class _Call : public pj::Call {
  78. public:
  79. _Call(sip::PjsuaCommunicator &comm, pj::Account &acc, int call_id = PJSUA_INVALID_ID)
  80. : pj::Call(acc, call_id),
  81. communicator(comm),
  82. account(acc) { }
  83. virtual void onCallState(pj::OnCallStateParam &prm) override;
  84. virtual void onCallMediaState(pj::OnCallMediaStateParam &prm) override;
  85. virtual void onDtmfDigit(pj::OnDtmfDigitParam &prm) override;
  86. virtual void playAudioFile(std::string file);
  87. virtual void playAudioFile(std::string file, bool in_chan);
  88. private:
  89. sip::PjsuaCommunicator &communicator;
  90. pj::Account &account;
  91. };
  92. class _Account : public pj::Account {
  93. public:
  94. _Account(sip::PjsuaCommunicator &comm, int max_calls)
  95. : communicator(comm) { this->max_calls = max_calls; }
  96. virtual void onRegState(pj::OnRegStateParam &prm) override;
  97. virtual void onIncomingCall(pj::OnIncomingCallParam &iprm) override;
  98. private:
  99. sip::PjsuaCommunicator &communicator;
  100. int active_calls = 0;
  101. int max_calls;
  102. friend class _Call;
  103. };
  104. void _Call::onCallState(pj::OnCallStateParam &prm) {
  105. auto ci = getInfo();
  106. communicator.logger.info("Call %d state=%s.", ci.id, ci.stateText.c_str());
  107. string address = ci.remoteUri;
  108. boost::replace_all(address, "<", "");
  109. boost::replace_all(address, ">", "");
  110. if (ci.state == PJSIP_INV_STATE_CONFIRMED) {
  111. auto msgText = "Incoming call from " + address + ".";
  112. // first, login to Mumble (only matters if MUM_DELAYED_CONNECT)
  113. communicator.calls[ci.id].onConnect();
  114. pj_thread_sleep(500); // sleep a moment to allow connection to stabilize
  115. communicator.logger.notice(msgText);
  116. communicator.calls[ci.id].sendUserStateStr(mumlib::UserState::COMMENT, msgText);
  117. communicator.calls[ci.id].onStateChange(msgText);
  118. pj_thread_sleep(500); // sleep a moment to allow connection to stabilize
  119. this->playAudioFile(communicator.file_welcome);
  120. communicator.got_dtmf = "";
  121. /*
  122. * if no pin is set, go ahead and turn off mute/deaf
  123. * otherwise, wait for pin to be entered
  124. */
  125. if ( communicator.pins.size() == 0 ) {
  126. // No PIN set... enter DTMF root menu and turn off mute/deaf
  127. communicator.dtmf_mode = DTMF_MODE_ROOT;
  128. // turning off mute automatically turns off deaf
  129. communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false);
  130. pj_thread_sleep(500); // sleep a moment to allow connection to stabilize
  131. this->playAudioFile(communicator.file_announce_new_caller, true);
  132. } else {
  133. // PIN set... enter DTMF unauth menu and play PIN prompt message
  134. communicator.dtmf_mode = DTMF_MODE_UNAUTH;
  135. communicator.calls[ci.id].joinDefaultChannel();
  136. pj_thread_sleep(500); // pause briefly after announcement
  137. this->playAudioFile(communicator.file_prompt_pin);
  138. }
  139. } else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
  140. auto &acc = dynamic_cast<_Account &>(account);
  141. /*
  142. * Not sure why we check acc.available, but with multi-call
  143. * functionality, this check doesn't work.
  144. */
  145. //if (not acc.available) {
  146. auto msgText = "Call from " + address + " finished.";
  147. communicator.calls[ci.id].mixer->clear();
  148. communicator.logger.notice(msgText);
  149. communicator.calls[ci.id].sendUserStateStr(mumlib::UserState::COMMENT, msgText);
  150. communicator.calls[ci.id].onStateChange(msgText);
  151. communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_DEAF, true);
  152. communicator.calls[ci.id].joinDefaultChannel();
  153. communicator.calls[ci.id].onDisconnect();
  154. //acc.available = true;
  155. acc.active_calls--;
  156. //}
  157. delete this;
  158. } else {
  159. communicator.logger.notice("MYDEBUG: unexpected state in onCallState() call:%d state:%d",
  160. ci.id, ci.state);
  161. }
  162. }
  163. void _Call::onCallMediaState(pj::OnCallMediaStateParam &prm) {
  164. auto ci = getInfo();
  165. if (ci.media.size() != 1) {
  166. throw sip::Exception("ci.media.size is not 1");
  167. }
  168. if (ci.media[0].status == PJSUA_CALL_MEDIA_ACTIVE) {
  169. auto *aud_med = static_cast<pj::AudioMedia *>(getMedia(0));
  170. communicator.calls[ci.id].media->startTransmit(*aud_med);
  171. aud_med->startTransmit(*communicator.calls[ci.id].media);
  172. } else if (ci.media[0].status == PJSUA_CALL_MEDIA_NONE) {
  173. dynamic_cast<_Account &>(account).active_calls++;
  174. }
  175. }
  176. void _Call::playAudioFile(std::string file) {
  177. this->playAudioFile(file, false); // default is NOT to echo to mumble
  178. }
  179. /* TODO:
  180. * - local deafen before playing and undeafen after?
  181. */
  182. void _Call::playAudioFile(std::string file, bool in_chan) {
  183. communicator.logger.info("Entered playAudioFile(%s)", file.c_str());
  184. pj::AudioMediaPlayer player;
  185. pj::MediaFormatAudio mfa;
  186. pj::AudioMediaPlayerInfo pinfo;
  187. int wavsize;
  188. int sleeptime;
  189. if ( ! pj_file_exists(file.c_str()) ) {
  190. communicator.logger.warn("File not found (%s)", file.c_str());
  191. return;
  192. }
  193. /* TODO: use some library to get the actual length in millisec
  194. *
  195. * This just gets the file size and divides by a constant to
  196. * estimate the length of the WAVE file in milliseconds.
  197. * This depends on the encoding bitrate, etc.
  198. */
  199. auto ci = getInfo();
  200. if (ci.media.size() != 1) {
  201. throw sip::Exception("ci.media.size is not 1");
  202. }
  203. if (ci.media[0].status == PJSUA_CALL_MEDIA_ACTIVE) {
  204. auto *aud_med = static_cast<pj::AudioMedia *>(getMedia(0));
  205. try {
  206. player.createPlayer(file, PJMEDIA_FILE_NO_LOOP);
  207. pinfo = player.getInfo();
  208. sleeptime = pinfo.sizeBytes / (pinfo.payloadBitsPerSample * 3);
  209. if ( in_chan ) { // choose the target sound output
  210. player.startTransmit(*communicator.calls[ci.id].media);
  211. } else {
  212. player.startTransmit(*aud_med);
  213. }
  214. pj_thread_sleep(sleeptime);
  215. if ( in_chan ) { // choose the target sound output
  216. player.stopTransmit(*communicator.calls[ci.id].media);
  217. } else {
  218. player.stopTransmit(*aud_med);
  219. }
  220. } catch (...) {
  221. communicator.logger.notice("Error playing file %s", file.c_str());
  222. }
  223. } else {
  224. communicator.logger.notice("Call not active - can't play file %s", file.c_str());
  225. }
  226. }
  227. void _Call::onDtmfDigit(pj::OnDtmfDigitParam &prm) {
  228. //communicator.logger.notice("DTMF digit '%s' (call %d).",
  229. // prm.digit.c_str(), getId());
  230. pj::CallOpParam param;
  231. auto ci = getInfo();
  232. std::string chanName;
  233. /*
  234. * DTMF CALLER MENU
  235. */
  236. switch ( communicator.dtmf_mode ) {
  237. case DTMF_MODE_UNAUTH:
  238. /*
  239. * IF UNAUTH, the only thing we allow is to authorize.
  240. */
  241. switch ( prm.digit[0] ) {
  242. case '#':
  243. /*
  244. * When user presses '#', test PIN entry
  245. */
  246. if ( communicator.pins.size() > 0 ) {
  247. if ( communicator.pins.count(communicator.got_dtmf) > 0 ) {
  248. communicator.logger.info("Caller entered correct PIN");
  249. communicator.dtmf_mode = DTMF_MODE_ROOT;
  250. communicator.logger.notice("MYDEBUG: %s:%s",
  251. communicator.got_dtmf.c_str(),
  252. communicator.pins[communicator.got_dtmf].c_str());
  253. communicator.calls[ci.id].joinOtherChannel(
  254. communicator.pins[communicator.got_dtmf]);
  255. this->playAudioFile(communicator.file_entering_channel);
  256. communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false);
  257. this->playAudioFile(communicator.file_announce_new_caller, true);
  258. } else {
  259. communicator.logger.info("Caller entered wrong PIN");
  260. this->playAudioFile(communicator.file_invalid_pin);
  261. if ( communicator.pin_fails++ >= MAX_PIN_FAILS ) {
  262. param.statusCode = PJSIP_SC_SERVICE_UNAVAILABLE;
  263. pj_thread_sleep(500); // pause before next announcement
  264. this->playAudioFile(communicator.file_goodbye);
  265. pj_thread_sleep(500); // pause before next announcement
  266. this->hangup(param);
  267. }
  268. this->playAudioFile(communicator.file_prompt_pin);
  269. }
  270. communicator.got_dtmf = "";
  271. }
  272. break;
  273. case '*':
  274. /*
  275. * Allow user to reset PIN entry by pressing '*'
  276. */
  277. communicator.got_dtmf = "";
  278. this->playAudioFile(communicator.file_prompt_pin);
  279. break;
  280. default:
  281. /*
  282. * In all other cases, add input digit to stack
  283. */
  284. communicator.got_dtmf = communicator.got_dtmf + prm.digit;
  285. if ( communicator.got_dtmf.size() > MAX_CALLER_PIN_LEN ) {
  286. // just drop 'em if too long
  287. param.statusCode = PJSIP_SC_SERVICE_UNAVAILABLE;
  288. this->playAudioFile(communicator.file_goodbye);
  289. pj_thread_sleep(500); // pause before next announcement
  290. this->hangup(param);
  291. }
  292. }
  293. break;
  294. case DTMF_MODE_ROOT:
  295. /*
  296. * User already authenticated; no data entry pending
  297. */
  298. switch ( prm.digit[0] ) {
  299. case '*':
  300. /*
  301. * Switch user to 'star' menu
  302. */
  303. communicator.dtmf_mode = DTMF_MODE_STAR;
  304. break;
  305. default:
  306. /*
  307. * Default is to ignore all digits in root
  308. */
  309. communicator.logger.info("Ignore DTMF digit '%s' in ROOT state", prm.digit.c_str());
  310. }
  311. break;
  312. case DTMF_MODE_STAR:
  313. /*
  314. * User already entered '*'; time to perform action
  315. */
  316. switch ( prm.digit[0] ) {
  317. case '5':
  318. // Mute line
  319. communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, true);
  320. this->playAudioFile(communicator.file_mute_on);
  321. break;
  322. case '6':
  323. // Un-mute line
  324. this->playAudioFile(communicator.file_mute_off);
  325. communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false);
  326. break;
  327. case '9':
  328. if ( communicator.pins.size() > 0 ) {
  329. communicator.dtmf_mode = DTMF_MODE_UNAUTH;
  330. communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_DEAF, true);
  331. communicator.calls[ci.id].joinDefaultChannel();
  332. this->playAudioFile(communicator.file_prompt_pin);
  333. } else {
  334. // we should have a 'not supported' message
  335. }
  336. break;
  337. case '0': // block these for the menu itself
  338. case '*':
  339. default:
  340. // play menu
  341. communicator.logger.info("Unsupported DTMF digit '%s' in state STAR", prm.digit.c_str());
  342. this->playAudioFile(communicator.file_menu);
  343. break;
  344. }
  345. /*
  346. * In any case, switch back to root after one digit
  347. */
  348. communicator.dtmf_mode = DTMF_MODE_ROOT;
  349. break;
  350. default:
  351. communicator.logger.info("Unexpected DTMF '%s' in unknown state '%d'", prm.digit.c_str(),
  352. communicator.dtmf_mode);
  353. }
  354. }
  355. void _Account::onRegState(pj::OnRegStateParam &prm) {
  356. pj::AccountInfo ai = getInfo();
  357. communicator.logger << log4cpp::Priority::INFO
  358. << (ai.regIsActive ? "Register:" : "Unregister:") << " code=" << prm.code;
  359. }
  360. void _Account::onIncomingCall(pj::OnIncomingCallParam &iprm) {
  361. auto *call = new _Call(communicator, *this, iprm.callId);
  362. string uri = call->getInfo().remoteUri;
  363. communicator.logger.info("Incoming call from %s.", uri.c_str());
  364. pj::CallOpParam param;
  365. if (communicator.uriValidator.validateUri(uri)) {
  366. if (active_calls < max_calls) {
  367. param.statusCode = PJSIP_SC_OK;
  368. active_calls++;
  369. } else {
  370. communicator.logger.notice("BUSY - reject incoming call from %s.", uri.c_str());
  371. param.statusCode = PJSIP_SC_OK;
  372. param.statusCode = PJSIP_SC_BUSY_EVERYWHERE;
  373. }
  374. call->answer(param);
  375. } else {
  376. communicator.logger.warn("Refusing call from %s.", uri.c_str());
  377. param.statusCode = PJSIP_SC_SERVICE_UNAVAILABLE;
  378. call->hangup(param);
  379. }
  380. }
  381. }
  382. sip::PjsuaCommunicator::PjsuaCommunicator(IncomingConnectionValidator &validator, int frameTimeLength, int maxCalls)
  383. : logger(log4cpp::Category::getInstance("SipCommunicator")),
  384. pjsuaLogger(log4cpp::Category::getInstance("Pjsua")),
  385. uriValidator(validator) {
  386. logWriter.reset(new sip::_LogWriter(pjsuaLogger));
  387. max_calls = maxCalls;
  388. endpoint.libCreate();
  389. pj::EpConfig endpointConfig;
  390. endpointConfig.uaConfig.userAgent = "Mumsi Mumble-SIP gateway";
  391. endpointConfig.uaConfig.maxCalls = maxCalls;
  392. endpointConfig.logConfig.writer = logWriter.get();
  393. endpointConfig.logConfig.level = 5;
  394. endpointConfig.medConfig.noVad = true;
  395. endpoint.libInit(endpointConfig);
  396. for(int i=0; i<maxCalls; ++i) {
  397. calls[i].index = i;
  398. pj_caching_pool_init(&(calls[i].cachingPool), &pj_pool_factory_default_policy, 0);
  399. calls[i].mixer.reset(new mixer::AudioFramesMixer(calls[i].cachingPool.factory));
  400. calls[i].media.reset(new _MumlibAudioMedia(i, *this, frameTimeLength));
  401. }
  402. logger.info("Created Pjsua communicator with frame length %d ms.", frameTimeLength);
  403. }
  404. void sip::PjsuaCommunicator::connect(
  405. std::string host,
  406. std::string user,
  407. std::string password,
  408. unsigned int port) {
  409. pj::TransportConfig transportConfig;
  410. transportConfig.port = port;
  411. endpoint.transportCreate(PJSIP_TRANSPORT_UDP, transportConfig); // todo try catch
  412. endpoint.libStart();
  413. pj_status_t status = pjsua_set_null_snd_dev();
  414. if (status != PJ_SUCCESS) {
  415. throw sip::Exception("error in pjsua_set_null_std_dev()", status);
  416. }
  417. registerAccount(host, user, password);
  418. }
  419. sip::PjsuaCommunicator::~PjsuaCommunicator() {
  420. endpoint.libDestroy();
  421. }
  422. void sip::PjsuaCommunicator::sendPcmSamples(int callId, int sessionId, int sequenceNumber, int16_t *samples, unsigned int length) {
  423. calls[callId].mixer->addFrameToBuffer(sessionId, sequenceNumber, samples, length);
  424. }
  425. pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_port *port, pjmedia_frame *frame) {
  426. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  427. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  428. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&(port->info));
  429. int call_id = (int) port->port_data.ldata;
  430. const int readSamples = calls[call_id].mixer->getMixedSamples(samples, count);
  431. if (readSamples < count) {
  432. pjsuaLogger.debug("Requested %d samples, available %d, filling remaining with zeros.",
  433. count, readSamples);
  434. for (int i = readSamples; i < count; ++i) {
  435. samples[i] = 0;
  436. }
  437. }
  438. return PJ_SUCCESS;
  439. }
  440. pj_status_t sip::PjsuaCommunicator::mediaPortPutFrame(pjmedia_port *port, pjmedia_frame *frame) {
  441. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  442. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info);
  443. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  444. int call_id = (int) port->port_data.ldata;
  445. if (count > 0) {
  446. pjsuaLogger.debug("Calling onIncomingPcmSamples with %d samples (call_id=%d).", count, call_id);
  447. this->calls[call_id].onIncomingPcmSamples(samples, count);
  448. }
  449. return PJ_SUCCESS;
  450. }
  451. void sip::PjsuaCommunicator::registerAccount(string host, string user, string password) {
  452. string uri = "sip:" + user + "@" + host;
  453. pj::AccountConfig accountConfig;
  454. accountConfig.idUri = uri;
  455. accountConfig.regConfig.registrarUri = "sip:" + host;
  456. pj::AuthCredInfo cred("digest", "*", user, 0, password);
  457. accountConfig.sipConfig.authCreds.push_back(cred);
  458. logger.info("Registering account for URI: %s.", uri.c_str());
  459. account.reset(new _Account(*this, max_calls));
  460. account->create(accountConfig);
  461. }