PjsuaCommunicator.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #include "PjsuaCommunicator.hpp"
  2. #include <pjlib.h>
  3. #include <pjsua-lib/pjsua.h>
  4. #include <boost/algorithm/string/replace.hpp>
  5. using namespace std;
  6. /**
  7. * This is global, because there's no way to pass it's value to onCallMediaState callback.
  8. */
  9. static int mediaPortSlot;
  10. static log4cpp::Category &pjLogger = log4cpp::Category::getInstance("PjSip");
  11. static void onCallMediaState(pjsua_call_id call_id) {
  12. pjsua_call_info ci;
  13. pjsua_call_get_info(call_id, &ci);
  14. if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
  15. pjsua_conf_connect(ci.conf_slot, mediaPortSlot);
  16. pjsua_conf_connect(mediaPortSlot, ci.conf_slot);
  17. }
  18. }
  19. static void onIncomingCall(pjsua_acc_id acc_id,
  20. pjsua_call_id call_id,
  21. pjsip_rx_data *rdata) {
  22. pjsua_call_info ci;
  23. PJ_UNUSED_ARG(acc_id);
  24. PJ_UNUSED_ARG(rdata);
  25. pjsua_call_get_info(call_id, &ci);
  26. pjLogger.notice("Incoming call from %s.", ci.remote_info.ptr);
  27. /* Automatically answer incoming calls with 200/OK */
  28. pjsua_call_answer(call_id, 200, NULL, NULL);
  29. }
  30. static void onCallState(pjsua_call_id call_id,
  31. pjsip_event *e) {
  32. pjsua_call_info ci;
  33. PJ_UNUSED_ARG(e);
  34. pjsua_call_get_info(call_id, &ci);
  35. pjLogger.notice("Call %d state=%s.", call_id, ci.state_text.ptr);
  36. }
  37. static void pjLogToLog4CppBridgeFunction(int level, const char *data, int len) {
  38. using namespace log4cpp;
  39. std::map<int, Priority::Value> prioritiesMap = {
  40. {1, Priority::ERROR},
  41. {2, Priority::WARN},
  42. {3, Priority::NOTICE},
  43. {4, Priority::INFO},
  44. {5, Priority::DEBUG},
  45. {6, Priority::DEBUG}
  46. };
  47. string message(data);
  48. message = message.substr(0, message.size() - 1); // remove newline
  49. pjLogger << prioritiesMap.at(level) << message;
  50. }
  51. sip::PjsuaCommunicator::PjsuaCommunicator()
  52. : logger(log4cpp::Category::getInstance("SipCommunicator")),
  53. callbackLogger(log4cpp::Category::getInstance("SipCommunicatorCallback")) {
  54. pj_status_t status;
  55. status = pjsua_create();
  56. if (status != PJ_SUCCESS) {
  57. throw sip::Exception("Error in pjsua_create()", status);
  58. }
  59. pj_log_set_log_func(pjLogToLog4CppBridgeFunction);
  60. pjsua_config generalConfig;
  61. pjsua_config_default(&generalConfig);
  62. string userAgent = "Mumsi Mumble-SIP Bridge";
  63. generalConfig.user_agent = toPjString(userAgent);
  64. generalConfig.max_calls = 1;
  65. generalConfig.cb.on_incoming_call = &onIncomingCall;
  66. generalConfig.cb.on_call_media_state = &onCallMediaState;
  67. generalConfig.cb.on_call_state = &onCallState;
  68. pjsua_logging_config logConfig;
  69. pjsua_logging_config_default(&logConfig);
  70. logConfig.cb = pjLogToLog4CppBridgeFunction;
  71. logConfig.console_level = 5;
  72. status = pjsua_init(&generalConfig, &logConfig, NULL);
  73. if (status != PJ_SUCCESS) {
  74. throw sip::Exception("Error in pjsua_init()", status);
  75. }
  76. pjsua_set_null_snd_dev();
  77. pj_caching_pool cachingPool;
  78. pj_caching_pool_init(&cachingPool, &pj_pool_factory_default_policy, 0);
  79. pj_pool_t *pool = pj_pool_create(&cachingPool.factory, "wav", 32768, 8192, nullptr);
  80. // todo calculate sizes
  81. pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff);
  82. mediaPort = createMediaPort();
  83. pjsua_conf_add_port(pool, mediaPort, &mediaPortSlot);
  84. }
  85. void sip::PjsuaCommunicator::connect(
  86. std::string host,
  87. std::string user,
  88. std::string password,
  89. unsigned int port) {
  90. pj_status_t status;
  91. pjsua_transport_config transportConfig;
  92. pjsua_transport_config_default(&transportConfig);
  93. transportConfig.port = port;
  94. status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transportConfig, NULL);
  95. if (status != PJ_SUCCESS) {
  96. throw sip::Exception("Error creating transport", status);
  97. }
  98. /* Initialization is done, now start sip */
  99. status = pjsua_start();
  100. if (status != PJ_SUCCESS) {
  101. throw sip::Exception("Error starting sip", status);
  102. }
  103. registerAccount(host, user, password);
  104. }
  105. sip::PjsuaCommunicator::~PjsuaCommunicator() {
  106. pjsua_destroy();
  107. }
  108. pjmedia_port *sip::PjsuaCommunicator::createMediaPort() {
  109. pjmedia_port *mp = new pjmedia_port();
  110. string name = "PjsuaMP";
  111. auto pjName = toPjString(name);
  112. pj_status_t status = pjmedia_port_info_init(&(mp->info),
  113. &pjName,
  114. PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'),
  115. SAMPLING_RATE,
  116. 1,
  117. 16,
  118. SAMPLING_RATE * 20 / 1000); // todo recalculate to match mumble specs
  119. if (status != PJ_SUCCESS) {
  120. throw sip::Exception("error while calling pjmedia_port_info_init().", status);
  121. }
  122. mp->get_frame = &MediaPort_getFrameRawCallback;
  123. mp->put_frame = &MediaPort_putFrameRawCallback;
  124. mp->port_data.pdata = this;
  125. return mp;
  126. }
  127. pj_status_t sip::MediaPort_getFrameRawCallback(pjmedia_port *port,
  128. pjmedia_frame *frame) {
  129. PjsuaCommunicator *communicator = static_cast<PjsuaCommunicator *>(port->port_data.pdata);
  130. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  131. return communicator->mediaPortGetFrame(frame);
  132. }
  133. pj_status_t sip::MediaPort_putFrameRawCallback(pjmedia_port *port,
  134. pjmedia_frame *frame) {
  135. PjsuaCommunicator *communicator = static_cast<PjsuaCommunicator *>(port->port_data.pdata);
  136. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  137. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info);
  138. frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
  139. communicator->mediaPortPutFrame(samples, count);
  140. return PJ_SUCCESS;
  141. }
  142. pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_frame *frame) {
  143. std::unique_lock<std::mutex> lock(inBuffAccessMutex);
  144. pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf);
  145. pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&mediaPort->info);
  146. pj_size_t availableSamples = pjmedia_circ_buf_get_len(inputBuff);
  147. const int samplesToRead = std::min(count, availableSamples);
  148. callbackLogger.debug("Pulling %d samples from in-buff.", samplesToRead);
  149. pjmedia_circ_buf_read(inputBuff, samples, samplesToRead);
  150. if (availableSamples < count) {
  151. callbackLogger.debug("Requested %d samples, available %d, filling remaining with zeros.", count,
  152. availableSamples);
  153. for (int i = samplesToRead; i < count; ++i) {
  154. samples[i] = 0;
  155. }
  156. }
  157. return PJ_SUCCESS;
  158. }
  159. void sip::PjsuaCommunicator::mediaPortPutFrame(pj_int16_t *samples, pj_size_t count) {
  160. if (count > 0) {
  161. callbackLogger.debug("Calling onIncomingPcmSamples with %d samples.", count);
  162. onIncomingPcmSamples(samples, count);
  163. }
  164. }
  165. void sip::PjsuaCommunicator::registerAccount(string host, string user, string password) {
  166. pjsua_acc_config accConfig;
  167. pjsua_acc_config_default(&accConfig);
  168. string uri = string("sip:") + user + "@" + host;
  169. string regUri = "sip:" + host;
  170. string scheme = "digest";
  171. pj_status_t status;
  172. status = pjsua_verify_sip_url(uri.c_str());
  173. if (status != PJ_SUCCESS) {
  174. throw sip::Exception("invalid URI format", status);
  175. }
  176. logger.info("Registering account for URI: %s.", uri.c_str());
  177. accConfig.id = toPjString(uri);
  178. accConfig.reg_uri = toPjString(regUri);
  179. accConfig.cred_count = 1;
  180. accConfig.cred_info[0].realm = toPjString(host);
  181. accConfig.cred_info[0].scheme = toPjString(scheme);
  182. accConfig.cred_info[0].username = toPjString(user);
  183. accConfig.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
  184. accConfig.cred_info[0].data = toPjString(password);
  185. logger.error("id:%s", accConfig.id.ptr);
  186. logger.error("reg_uri:%s", accConfig.reg_uri.ptr);
  187. logger.error("realm:%s", accConfig.cred_info[0].realm.ptr);
  188. logger.error("scheme:%s", accConfig.cred_info[0].scheme.ptr);
  189. logger.error("username:%s", accConfig.cred_info[0].username.ptr);
  190. logger.error("data:%s", accConfig.cred_info[0].data.ptr);
  191. pjsua_acc_id acc_id;
  192. status = pjsua_acc_add(&accConfig, PJ_TRUE, &acc_id);
  193. if (status != PJ_SUCCESS) {
  194. throw sip::Exception("failed to register account", status);
  195. }
  196. }
  197. void sip::PjsuaCommunicator::sendPcmSamples(int16_t *samples, unsigned int length) {
  198. std::unique_lock<std::mutex> lock(inBuffAccessMutex);
  199. callbackLogger.debug("Pushing %d samples to in-buff.", length);
  200. pjmedia_circ_buf_write(inputBuff, samples, length);
  201. }