Clean up project.
This commit is contained in:
		
							parent
							
								
									0189d0f8ac
								
							
						
					
					
						commit
						d4a01348e0
					
				| @ -9,12 +9,9 @@ find_package(PkgConfig REQUIRED) | ||||
| 
 | ||||
| pkg_check_modules(PJSIP "libpjproject") | ||||
| pkg_check_modules(LOG4CPP "log4cpp") | ||||
| pkg_check_modules(SNDFILE "sndfile") | ||||
| 
 | ||||
| 
 | ||||
| include_directories(${PJSIP_INCLUDE_DIRS}) | ||||
| include_directories(${LOG4CPP_INCLUDE_DIRS}) | ||||
| include_directories(${SNDFILE_INCLUDE_DIRS}) | ||||
| include_directories(../mumlib/include) | ||||
| 
 | ||||
| link_directories(../mumlib/build) | ||||
| @ -24,7 +21,7 @@ set(SOURCE_FILES | ||||
|         PjsuaCommunicator.hpp | ||||
|         MumbleCommunicator.cpp | ||||
|         MumbleCommunicator.hpp | ||||
|         ISamplesBuffer.hpp | ||||
|         ICommunicator.hpp | ||||
|         Configuration.cpp | ||||
|         Configuration.hpp) | ||||
| 
 | ||||
| @ -33,10 +30,8 @@ set(SOURCE_FILES | ||||
| #) | ||||
| 
 | ||||
| add_executable(mumsi ${SOURCE_FILES} main.cpp) | ||||
| target_link_libraries(mumsi ${OPUS_LIBRARIES}) | ||||
| target_link_libraries(mumsi ${PJSIP_LIBRARIES}) | ||||
| target_link_libraries(mumsi ${LOG4CPP_LIBRARIES}) | ||||
| target_link_libraries(mumsi ${SNDFILE_LIBRARIES}) | ||||
| target_link_libraries(mumsi ${Boost_LIBRARIES}) | ||||
| target_link_libraries(mumsi mumlib) | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| #ifndef CONFIGURATION_HPP_ | ||||
| #define CONFIGURATION_HPP_ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include <stdexcept> | ||||
| @ -35,5 +34,3 @@ namespace config { | ||||
|         ConfigurationImpl *impl; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIGURATION_HPP_ */ | ||||
|  | ||||
							
								
								
									
										17
									
								
								ICommunicator.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								ICommunicator.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <inttypes.h> | ||||
| 
 | ||||
| class ICommunicator { | ||||
| public: | ||||
|     /**
 | ||||
|      * Send samples through the Communicator. | ||||
|      */ | ||||
|     virtual void sendPcmSamples(int16_t *samples, unsigned int length) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * This callback is called when Communicator has received samples. | ||||
|      */ | ||||
|     std::function<void(int16_t *, int)> onIncomingPcmSamples; | ||||
| }; | ||||
| @ -1,14 +0,0 @@ | ||||
| #ifndef MUMSI_ISAMPLESBUFFER_HPP | ||||
| #define MUMSI_ISAMPLESBUFFER_HPP | ||||
| 
 | ||||
| #include <inttypes.h> | ||||
| 
 | ||||
| class ISamplesBuffer { | ||||
| public: | ||||
|     virtual void pushSamples(int16_t *samples, unsigned int length) = 0; | ||||
| 
 | ||||
| //    virtual unsigned int pullSamples(int16_t *samples, unsigned int length, bool waitWhenEmpty) = 0;
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif //MUMSI_ISAMPLESBUFFER_HPP
 | ||||
| @ -12,22 +12,21 @@ namespace mumble { | ||||
|         virtual void audio( | ||||
|                 int16_t *pcm_data, | ||||
|                 uint32_t pcm_data_size) { | ||||
|             communicator->samplesBuffer.pushSamples(pcm_data, pcm_data_size); | ||||
|             communicator->onIncomingPcmSamples(pcm_data, pcm_data_size); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| mumble::MumbleCommunicator::MumbleCommunicator( | ||||
|         boost::asio::io_service &ioService, | ||||
|         ISamplesBuffer &samplesBuffer, | ||||
| mumble::MumbleCommunicator::MumbleCommunicator(boost::asio::io_service &ioService) | ||||
|         : ioService(ioService), | ||||
|           logger(log4cpp::Category::getInstance("MumbleCommunicator")) { | ||||
| } | ||||
| 
 | ||||
| void mumble::MumbleCommunicator::connect( | ||||
|         std::string user, | ||||
|         std::string password, | ||||
|         std::string host, | ||||
|         int port) : ioService(ioService), | ||||
|                     samplesBuffer(samplesBuffer), | ||||
|                     logger(log4cpp::Category::getInstance("MumbleCommunicator")) { | ||||
| 
 | ||||
|     quit = false; | ||||
|         int port) { | ||||
| 
 | ||||
|     callback.reset(new MumlibCallback()); | ||||
| 
 | ||||
| @ -38,7 +37,7 @@ mumble::MumbleCommunicator::MumbleCommunicator( | ||||
|     mum->connect(host, port, user, password); | ||||
| } | ||||
| 
 | ||||
| void mumble::MumbleCommunicator::sendAudioFrame(int16_t *samples, int length) { | ||||
| void mumble::MumbleCommunicator::sendPcmSamples(int16_t *samples, unsigned int length) { | ||||
|     mum->sendAudioData(samples, length); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,14 +1,12 @@ | ||||
| #ifndef MUMSI_MUMBLECOMMUNICATOR_HPP | ||||
| #define MUMSI_MUMBLECOMMUNICATOR_HPP | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ICommunicator.hpp" | ||||
| 
 | ||||
| #include "ISamplesBuffer.hpp" | ||||
| #include <mumlib.hpp> | ||||
| 
 | ||||
| #include <string> | ||||
| #include <stdexcept> | ||||
| #include <log4cpp/Category.hh> | ||||
| #include <sndfile.hh> | ||||
| #include <thread> | ||||
| 
 | ||||
| namespace mumble { | ||||
| 
 | ||||
| @ -19,11 +17,12 @@ namespace mumble { | ||||
| 
 | ||||
|     class MumlibCallback; | ||||
| 
 | ||||
|     class MumbleCommunicator { | ||||
|     class MumbleCommunicator : public ICommunicator { | ||||
|     public: | ||||
|         MumbleCommunicator( | ||||
|                 boost::asio::io_service &ioService, | ||||
|                 ISamplesBuffer &samplesBuffer, | ||||
|                 boost::asio::io_service &ioService); | ||||
| 
 | ||||
|         void connect( | ||||
|                 std::string user, | ||||
|                 std::string password, | ||||
|                 std::string host, | ||||
| @ -31,31 +30,17 @@ namespace mumble { | ||||
| 
 | ||||
|         ~MumbleCommunicator(); | ||||
| 
 | ||||
| //        void senderThreadFunction();
 | ||||
| 
 | ||||
|         //void receiveAudioFrameCallback(uint8_t *audio_data, uint32_t audio_data_size);
 | ||||
| 
 | ||||
|         void sendAudioFrame(int16_t *samples, int length); | ||||
|         virtual void sendPcmSamples(int16_t *samples, unsigned int length); | ||||
| 
 | ||||
|     public: | ||||
|         boost::asio::io_service &ioService; | ||||
| 
 | ||||
|         log4cpp::Category &logger; | ||||
| 
 | ||||
|         ISamplesBuffer &samplesBuffer; | ||||
| 
 | ||||
|         std::shared_ptr<mumlib::Mumlib> mum; | ||||
| 
 | ||||
|         std::unique_ptr<std::thread> senderThread; | ||||
| 
 | ||||
|         SndfileHandle fileHandle; | ||||
| 
 | ||||
|         std::unique_ptr<MumlibCallback> callback; | ||||
| 
 | ||||
|         bool quit; | ||||
| 
 | ||||
|         friend class MumlibCallback; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif //MUMSI_MUMBLECOMMUNICATOR_HPP
 | ||||
|  | ||||
| @ -3,22 +3,17 @@ | ||||
| 
 | ||||
| #include <pjlib.h> | ||||
| #include <pjsua-lib/pjsua.h> | ||||
| 
 | ||||
| #include <functional> | ||||
| 
 | ||||
| #include <cstring> | ||||
| 
 | ||||
| //todo wywalić
 | ||||
| #define THIS_FILE "mumsi" | ||||
| #include <boost/algorithm/string/replace.hpp> | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * This is global, because there's no way to pass it's value to onCallMediaState callback. | ||||
|  */ | ||||
| static int mediaPortSlot; | ||||
| 
 | ||||
| static log4cpp::Category &pjLogger = log4cpp::Category::getInstance("PjSip"); | ||||
| 
 | ||||
| 
 | ||||
| static void onCallMediaState(pjsua_call_id call_id) { | ||||
|     pjsua_call_info ci; | ||||
| @ -41,9 +36,7 @@ static void onIncomingCall(pjsua_acc_id acc_id, | ||||
| 
 | ||||
|     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)); | ||||
|     pjLogger.notice("Incoming call from %s.", ci.remote_info.ptr); | ||||
| 
 | ||||
|     /* Automatically answer incoming calls with 200/OK */ | ||||
|     pjsua_call_answer(call_id, 200, NULL, NULL); | ||||
| @ -56,23 +49,30 @@ static void onCallState(pjsua_call_id call_id, | ||||
|     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)); | ||||
|     pjLogger.notice("Call %d state=%s.", call_id, ci.state_text.ptr); | ||||
| } | ||||
| 
 | ||||
| static void pjLogToLog4CppBridgeFunction(int level, const char *data, int len) { | ||||
|     using namespace log4cpp; | ||||
|     std::map<int, Priority::Value> prioritiesMap = { | ||||
|             {1, Priority::ERROR}, | ||||
|             {2, Priority::WARN}, | ||||
|             {3, Priority::NOTICE}, | ||||
|             {4, Priority::INFO}, | ||||
|             {5, Priority::DEBUG}, | ||||
|             {6, Priority::DEBUG} | ||||
|     }; | ||||
| 
 | ||||
|     string message(data); | ||||
| 
 | ||||
|     message = message.substr(0, message.size() - 1); // remove newline
 | ||||
| 
 | ||||
|     pjLogger << prioritiesMap.at(level) << message; | ||||
| } | ||||
| 
 | ||||
| sip::PjsuaCommunicator::PjsuaCommunicator() | ||||
|         : logger(log4cpp::Category::getInstance("SipCommunicator")), | ||||
|           callbackLogger(log4cpp::Category::getInstance("SipCommunicatorCallback")) { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void sip::PjsuaCommunicator::connect( | ||||
|         std::string host, | ||||
|         std::string user, | ||||
|         std::string password, | ||||
|         unsigned int port) { | ||||
| 
 | ||||
|     pj_status_t status; | ||||
| 
 | ||||
|     status = pjsua_create(); | ||||
| @ -80,6 +80,8 @@ void sip::PjsuaCommunicator::connect( | ||||
|         throw sip::Exception("Error in pjsua_create()", status); | ||||
|     } | ||||
| 
 | ||||
|     pj_log_set_log_func(pjLogToLog4CppBridgeFunction); | ||||
| 
 | ||||
|     pjsua_config generalConfig; | ||||
|     pjsua_config_default(&generalConfig); | ||||
| 
 | ||||
| @ -90,11 +92,10 @@ void sip::PjsuaCommunicator::connect( | ||||
|     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; | ||||
|     logConfig.cb = pjLogToLog4CppBridgeFunction; | ||||
|     logConfig.console_level = 5; | ||||
| 
 | ||||
|     status = pjsua_init(&generalConfig, &logConfig, NULL); | ||||
|     if (status != PJ_SUCCESS) { | ||||
| @ -103,7 +104,25 @@ void sip::PjsuaCommunicator::connect( | ||||
| 
 | ||||
|     pjsua_set_null_snd_dev(); | ||||
| 
 | ||||
|     /* Add UDP transport. */ | ||||
|     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", 32768, 8192, nullptr); | ||||
| 
 | ||||
|     // todo calculate sizes
 | ||||
|     pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff); | ||||
| 
 | ||||
|     mediaPort = createMediaPort(); | ||||
| 
 | ||||
|     pjsua_conf_add_port(pool, mediaPort, &mediaPortSlot); | ||||
| } | ||||
| 
 | ||||
| void sip::PjsuaCommunicator::connect( | ||||
|         std::string host, | ||||
|         std::string user, | ||||
|         std::string password, | ||||
|         unsigned int port) { | ||||
|     pj_status_t status; | ||||
| 
 | ||||
|     pjsua_transport_config transportConfig; | ||||
|     pjsua_transport_config_default(&transportConfig); | ||||
| 
 | ||||
| @ -114,18 +133,6 @@ void sip::PjsuaCommunicator::connect( | ||||
|         throw sip::Exception("Error creating transport", status); | ||||
|     } | ||||
| 
 | ||||
|     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", 32768, 8192, nullptr); | ||||
| 
 | ||||
|     // create circular buffers
 | ||||
|     pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff); | ||||
|     pjmedia_circ_buf_create(pool, 960 * 10, &outputBuff); | ||||
| 
 | ||||
|     mediaPort = createMediaPort(); | ||||
| 
 | ||||
|     pjsua_conf_add_port(pool, mediaPort, &mediaPortSlot); | ||||
| 
 | ||||
|     /* Initialization is done, now start sip */ | ||||
|     status = pjsua_start(); | ||||
|     if (status != PJ_SUCCESS) { | ||||
| @ -210,16 +217,9 @@ pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_frame *frame) { | ||||
| } | ||||
| 
 | ||||
| void sip::PjsuaCommunicator::mediaPortPutFrame(pj_int16_t *samples, pj_size_t count) { | ||||
| //    std::unique_lock<std::mutex> lock(outBuffAccessMutex);
 | ||||
| //
 | ||||
| //    callbackLogger.debug("Pushing %d samples to out-buff.", count);
 | ||||
| //    pjmedia_circ_buf_write(outputBuff, samples, count);
 | ||||
| //
 | ||||
| //    lock.unlock();
 | ||||
| //
 | ||||
| //    outBuffCondVar.notify_all();
 | ||||
|     if (count > 0) { | ||||
|         onIncomingSamples(samples, count); | ||||
|         callbackLogger.debug("Calling onIncomingPcmSamples with %d samples.", count); | ||||
|         onIncomingPcmSamples(samples, count); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -228,7 +228,18 @@ void sip::PjsuaCommunicator::registerAccount(string host, string user, string pa | ||||
|     pjsua_acc_config accConfig; | ||||
|     pjsua_acc_config_default(&accConfig); | ||||
| 
 | ||||
|     accConfig.id = toPjString(string("sip:") + user + "@" + host); | ||||
|     string uri = string("sip:") + user + "@" + host; | ||||
| 
 | ||||
|     pj_status_t status; | ||||
| 
 | ||||
|     status = pjsua_verify_sip_url(uri.c_str()); | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw sip::Exception("failed to register account", status); | ||||
|     } | ||||
| 
 | ||||
|     logger.info("Registering account for URI: %s.", uri.c_str()); | ||||
| 
 | ||||
|     accConfig.id = toPjString(uri); | ||||
|     accConfig.reg_uri = toPjString(string("sip:") + host); | ||||
| 
 | ||||
|     accConfig.cred_count = 1; | ||||
| @ -240,32 +251,14 @@ void sip::PjsuaCommunicator::registerAccount(string host, string user, string pa | ||||
| 
 | ||||
|     pjsua_acc_id acc_id; | ||||
| 
 | ||||
|     pj_status_t status = pjsua_acc_add(&accConfig, PJ_TRUE, &acc_id); | ||||
|     status = pjsua_acc_add(&accConfig, PJ_TRUE, &acc_id); | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw sip::Exception("failed to register account", status); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sip::PjsuaCommunicator::pushSamples(int16_t *samples, unsigned int length) { | ||||
| void sip::PjsuaCommunicator::sendPcmSamples(int16_t *samples, unsigned int length) { | ||||
|     std::unique_lock<std::mutex> lock(inBuffAccessMutex); | ||||
|     callbackLogger.debug("Pushing %d samples to in-buff.", length); | ||||
|     pjmedia_circ_buf_write(inputBuff, samples, length); | ||||
| } | ||||
| 
 | ||||
| //unsigned int sip::PjsuaCommunicator::pullSamples(int16_t *samples, unsigned int length, bool waitWhenEmpty) {
 | ||||
| //    std::unique_lock<std::mutex> lock(outBuffAccessMutex);
 | ||||
| //
 | ||||
| //    unsigned int availableSamples;
 | ||||
| //
 | ||||
| //    while ((availableSamples = pjmedia_circ_buf_get_len(inputBuff)) < length) {
 | ||||
| //        callbackLogger.debug("Not enough samples in buffer: %d, requested %d. Waiting.", availableSamples, length);
 | ||||
| //        outBuffCondVar.wait(lock);
 | ||||
| //    }
 | ||||
| //
 | ||||
| //    const int samplesToRead = std::min(length, availableSamples);
 | ||||
| //
 | ||||
| //    callbackLogger.debug("Pulling %d samples from out-buff.", samplesToRead);
 | ||||
| //    pjmedia_circ_buf_read(inputBuff, samples, samplesToRead);
 | ||||
| //
 | ||||
| //    return samplesToRead;
 | ||||
| //}
 | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| #ifndef MUMSI_PJSUACOMMUNICATOR_HPP | ||||
| #define MUMSI_PJSUACOMMUNICATOR_HPP | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ISamplesBuffer.hpp" | ||||
| #include "ICommunicator.hpp" | ||||
| 
 | ||||
| #include <pjmedia.h> | ||||
| 
 | ||||
| @ -44,7 +43,7 @@ namespace sip { | ||||
| 
 | ||||
|     pj_status_t MediaPort_putFrameRawCallback(pjmedia_port *port, pjmedia_frame *frame); | ||||
| 
 | ||||
|     class PjsuaCommunicator : public ISamplesBuffer { | ||||
|     class PjsuaCommunicator : public ICommunicator { | ||||
|     public: | ||||
|         PjsuaCommunicator(); | ||||
| 
 | ||||
| @ -56,11 +55,7 @@ namespace sip { | ||||
| 
 | ||||
|         ~PjsuaCommunicator(); | ||||
| 
 | ||||
|         virtual void pushSamples(int16_t *samples, unsigned int length); | ||||
| 
 | ||||
|         std::function<void(int16_t *, int)> onIncomingSamples; | ||||
| 
 | ||||
| //        virtual unsigned int pullSamples(int16_t *samples, unsigned int length, bool waitWhenEmpty);
 | ||||
|         virtual void sendPcmSamples(int16_t *samples, unsigned int length); | ||||
| 
 | ||||
|     private: | ||||
|         log4cpp::Category &logger; | ||||
| @ -70,14 +65,9 @@ namespace sip { | ||||
|         pjmedia_port *mediaPort; | ||||
| 
 | ||||
|         pjmedia_circ_buf *inputBuff; | ||||
|         pjmedia_circ_buf *outputBuff; | ||||
| 
 | ||||
|         std::mutex inBuffAccessMutex; | ||||
| 
 | ||||
|         std::mutex outBuffAccessMutex; | ||||
|         std::condition_variable outBuffCondVar; | ||||
| 
 | ||||
| 
 | ||||
|         // todo make it completely stateless
 | ||||
|         pjmedia_port *createMediaPort(); | ||||
| 
 | ||||
| @ -101,5 +91,3 @@ namespace sip { | ||||
|     }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif //MUMSI_PJSUACOMMUNICATOR_HPP
 | ||||
|  | ||||
							
								
								
									
										24
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								main.cpp
									
									
									
									
									
								
							| @ -26,27 +26,31 @@ int main(int argc, char *argv[]) { | ||||
| 
 | ||||
|     sip::PjsuaCommunicator pjsuaCommunicator; | ||||
| 
 | ||||
|     mumble::MumbleCommunicator mumbleCommunicator( | ||||
|             ioService, | ||||
|             pjsuaCommunicator, | ||||
|     mumble::MumbleCommunicator mumbleCommunicator(ioService); | ||||
| 
 | ||||
|     using namespace std::placeholders; | ||||
|     pjsuaCommunicator.onIncomingPcmSamples = std::bind( | ||||
|             &mumble::MumbleCommunicator::sendPcmSamples, | ||||
|             &mumbleCommunicator, | ||||
|             _1, _2); | ||||
| 
 | ||||
|     mumbleCommunicator.onIncomingPcmSamples = std::bind( | ||||
|             &sip::PjsuaCommunicator::sendPcmSamples, | ||||
|             &pjsuaCommunicator, | ||||
|             _1, _2); | ||||
| 
 | ||||
|     mumbleCommunicator.connect( | ||||
|             conf.getString("mumble.user"), | ||||
|             conf.getString("mumble.password"), | ||||
|             conf.getString("mumble.host"), | ||||
|             conf.getInt("mumble.port")); | ||||
| 
 | ||||
|     using namespace std::placeholders; | ||||
|     pjsuaCommunicator.onIncomingSamples = std::bind( | ||||
|             &mumble::MumbleCommunicator::sendAudioFrame, | ||||
|             &mumbleCommunicator, | ||||
|             _1, _2); | ||||
| 
 | ||||
|     pjsuaCommunicator.connect( | ||||
|             conf.getString("sip.host"), | ||||
|             conf.getString("sip.user"), | ||||
|             conf.getString("sip.password"), | ||||
|             conf.getInt("sip.port")); | ||||
| 
 | ||||
| 
 | ||||
|     logger.info("Application started."); | ||||
| 
 | ||||
|     ioService.run(); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Michał Słomkowski
						Michał Słomkowski