Refactor, not working properly.
This commit is contained in:
		
							parent
							
								
									cdd782c4ce
								
							
						
					
					
						commit
						2bd1ba3fd2
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| *.iml | ||||
| .idea/ | ||||
| @ -1,25 +0,0 @@ | ||||
| #ifndef MUMSI_ABSTRACTCOMMUNICATOR_HPP | ||||
| #define MUMSI_ABSTRACTCOMMUNICATOR_HPP | ||||
| 
 | ||||
| #include "SoundSampleQueue.hpp" | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #define SOUND_SAMPLE_TYPE int16_t | ||||
| 
 | ||||
| class AbstractCommunicator { | ||||
| public: | ||||
| 
 | ||||
|     virtual void loop() = 0; | ||||
| 
 | ||||
| protected: | ||||
|     AbstractCommunicator( | ||||
|             SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue, | ||||
|             SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue) | ||||
|             : inputQueue(inputQueue), | ||||
|               outputQueue(outputQueue) { } | ||||
| 
 | ||||
|     SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue; | ||||
|     SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue; | ||||
| }; | ||||
| 
 | ||||
| #endif //MUMSI_ABSTRACTCOMMUNICATOR_HPP
 | ||||
| @ -23,15 +23,14 @@ link_directories(libmumble) | ||||
| 
 | ||||
| set(SOURCE_FILES | ||||
|         PjsuaCommunicator.cpp | ||||
|         PjsuaMediaPort.cpp | ||||
|         PjsuaCommunicator.hpp | ||||
|         MumbleCommunicator.cpp | ||||
|         SoundSampleQueue.cpp | ||||
|         AbstractCommunicator.hpp | ||||
| ) | ||||
|         MumbleCommunicator.hpp | ||||
|         ISamplesBuffer.hpp) | ||||
| 
 | ||||
| set(TEST_SOURCE_FILES | ||||
|         SoundSampleQueueTest.cpp | ||||
| ) | ||||
| #set(TEST_SOURCE_FILES | ||||
| #        SoundSampleQueueTest.cpp | ||||
| #) | ||||
| 
 | ||||
| add_executable(mumsi ${SOURCE_FILES} main.cpp) | ||||
| target_link_libraries(mumsi ${OPUS_LIBRARIES}) | ||||
| @ -41,10 +40,10 @@ target_link_libraries(mumsi ${SNDFILE_LIBRARIES}) | ||||
| target_link_libraries(mumsi ${Boost_LIBRARIES}) | ||||
| target_link_libraries(mumsi mumble) | ||||
| 
 | ||||
| add_executable(mumsi_test ${SOURCE_FILES} ${TEST_SOURCE_FILES} test_main.cpp) | ||||
| target_link_libraries(mumsi_test ${OPUS_LIBRARIES}) | ||||
| target_link_libraries(mumsi_test ${PJSIP_LIBRARIES}) | ||||
| target_link_libraries(mumsi_test ${LOG4CPP_LIBRARIES}) | ||||
| target_link_libraries(mumsi_test ${SNDFILE_LIBRARIES}) | ||||
| target_link_libraries(mumsi_test ${Boost_LIBRARIES}) | ||||
| target_link_libraries(mumsi_test mumble) | ||||
| #add_executable(mumsi_test ${SOURCE_FILES} ${TEST_SOURCE_FILES} test_main.cpp) | ||||
| #target_link_libraries(mumsi_test ${OPUS_LIBRARIES}) | ||||
| #target_link_libraries(mumsi_test ${PJSIP_LIBRARIES}) | ||||
| #target_link_libraries(mumsi_test ${LOG4CPP_LIBRARIES}) | ||||
| #target_link_libraries(mumsi_test ${SNDFILE_LIBRARIES}) | ||||
| #target_link_libraries(mumsi_test ${Boost_LIBRARIES}) | ||||
| #target_link_libraries(mumsi_test mumble) | ||||
|  | ||||
							
								
								
									
										14
									
								
								ISamplesBuffer.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ISamplesBuffer.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| #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
 | ||||
| @ -37,7 +37,7 @@ void mumble::MumbleCommunicator::receiveAudioFrameCallback(uint8_t *audio_data, | ||||
|         logger.debug("Received %d bytes of Opus data (seq %ld), decoded to %d bytes. Push it to outputQueue.", | ||||
|                      opusDataLength, sequenceNumber, decodedSamples); | ||||
| 
 | ||||
|         outputQueue.push_back(pcmData, decodedSamples); | ||||
|         samplesBuffer.pushSamples(pcmData, decodedSamples); | ||||
| 
 | ||||
|     } else { | ||||
|         logger.warn("Received %d bytes of non-recognisable audio data.", audio_data_size); | ||||
| @ -63,15 +63,16 @@ static int verify_cert(uint8_t *, uint32_t) { | ||||
| } | ||||
| 
 | ||||
| mumble::MumbleCommunicator::MumbleCommunicator( | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue, | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue, | ||||
|         ISamplesBuffer &samplesBuffer, | ||||
|         std::string user, | ||||
|         std::string password, | ||||
|         std::string host, | ||||
|         int port) : AbstractCommunicator(inputQueue, outputQueue), | ||||
|         int port) : samplesBuffer(samplesBuffer), | ||||
|                     outgoingAudioSequenceNumber(1), | ||||
|                     logger(log4cpp::Category::getInstance("MumbleCommunicator")) { | ||||
| 
 | ||||
|     quit = false; | ||||
| 
 | ||||
|     opusDecoder = opus_decoder_create(SAMPLE_RATE, 1, nullptr); //todo grab error
 | ||||
| 
 | ||||
|     opusEncoder = opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, nullptr); | ||||
| @ -110,31 +111,12 @@ mumble::MumbleCommunicator::~MumbleCommunicator() { | ||||
| } | ||||
| 
 | ||||
| void mumble::MumbleCommunicator::loop() { | ||||
|     int quit = 0; | ||||
|     while (quit == 0) { | ||||
| 
 | ||||
|         opus_int16 pcmData[1024]; | ||||
|         unsigned char outputBuffer[1024]; | ||||
| //        int pcmLength = inputQueue.pop(pcmData, 960);
 | ||||
| //
 | ||||
| //        logger.debug("Pop %d samples from inputQueue.", pcmLength);
 | ||||
| //
 | ||||
| //        if (pcmLength > 0) {
 | ||||
| //            int encodedSamples = opus_encode(opusEncoder, pcmData, pcmLength, outputBuffer, sizeof(outputBuffer));
 | ||||
| //
 | ||||
| //            if (encodedSamples < 1) {
 | ||||
| //                logger.warn("opus_encode returned %d: %s", encodedSamples, opus_strerror(encodedSamples));
 | ||||
| //            } else {
 | ||||
| //                logger.debug("Sending %d bytes of Opus audio data (seq %d).", encodedSamples,
 | ||||
| //                             outgoingAudioSequenceNumber);
 | ||||
| //
 | ||||
| //                mumble_send_audio_data(mumble, outgoingAudioSequenceNumber, outputBuffer, encodedSamples);
 | ||||
| //
 | ||||
| //                outgoingAudioSequenceNumber += 1;
 | ||||
| //            }
 | ||||
| //        }
 | ||||
|     senderThread.reset(new std::thread(&MumbleCommunicator::senderThreadFunction, this)); | ||||
| 
 | ||||
|     while (!quit) { | ||||
|         int status = mumble_tick(mumble); | ||||
|         logger.debug("tick"); | ||||
|         if (status < 0) { | ||||
|             throw mumble::Exception("mumble_tick status " + status); | ||||
|         } | ||||
| @ -144,3 +126,27 @@ void mumble::MumbleCommunicator::loop() { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void mumble::MumbleCommunicator::senderThreadFunction() { | ||||
|     while (!quit) { | ||||
|         opus_int16 pcmData[1024]; | ||||
|         unsigned char outputBuffer[1024]; | ||||
| 
 | ||||
|         int pcmLength = samplesBuffer.pullSamples(pcmData, 960, true); | ||||
| 
 | ||||
|         logger.debug("Pop %d samples from inputQueue.", pcmLength); | ||||
| 
 | ||||
|         int encodedSamples = opus_encode(opusEncoder, pcmData, pcmLength, outputBuffer, sizeof(outputBuffer)); | ||||
| 
 | ||||
|         if (encodedSamples < 1) { | ||||
|             logger.warn("opus_encode returned %d: %s", encodedSamples, opus_strerror(encodedSamples)); | ||||
|         } else { | ||||
| //            logger.debug("Sending %d bytes of Opus audio data (seq %d).", encodedSamples,
 | ||||
| //                         outgoingAudioSequenceNumber);
 | ||||
| //
 | ||||
| //            //todo to powinno dać się bezpiecznie wykonać w osobnym wątku
 | ||||
| //            mumble_send_audio_data(mumble, outgoingAudioSequenceNumber, outputBuffer, encodedSamples);
 | ||||
| //
 | ||||
| //            outgoingAudioSequenceNumber += 1;
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #ifndef MUMSI_MUMBLECOMMUNICATOR_HPP | ||||
| #define MUMSI_MUMBLECOMMUNICATOR_HPP | ||||
| 
 | ||||
| #include "AbstractCommunicator.hpp" | ||||
| #include "ISamplesBuffer.hpp" | ||||
| 
 | ||||
| extern "C" { | ||||
| #include "libmumble.h" | ||||
| @ -12,6 +12,7 @@ extern "C" { | ||||
| #include <opus.h> | ||||
| #include <log4cpp/Category.hh> | ||||
| #include <sndfile.hh> | ||||
| #include <thread> | ||||
| 
 | ||||
| namespace mumble { | ||||
| 
 | ||||
| @ -22,11 +23,10 @@ namespace mumble { | ||||
|         Exception(const char *message) : std::runtime_error(message) { } | ||||
|     }; | ||||
| 
 | ||||
|     class MumbleCommunicator : public AbstractCommunicator { | ||||
|     class MumbleCommunicator { | ||||
|     public: | ||||
|         MumbleCommunicator( | ||||
|                 SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue, | ||||
|                 SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue, | ||||
|                 ISamplesBuffer &samplesBuffer, | ||||
|                 std::string user, | ||||
|                 std::string password, | ||||
|                 std::string host, | ||||
| @ -36,11 +36,17 @@ namespace mumble { | ||||
| 
 | ||||
|         void loop(); | ||||
| 
 | ||||
|         void senderThreadFunction(); | ||||
| 
 | ||||
|         void receiveAudioFrameCallback(uint8_t *audio_data, uint32_t audio_data_size); | ||||
| 
 | ||||
|     private: | ||||
|         log4cpp::Category &logger; | ||||
| 
 | ||||
|         ISamplesBuffer &samplesBuffer; | ||||
| 
 | ||||
|         std::unique_ptr<std::thread> senderThread; | ||||
| 
 | ||||
|         mumble_struct *mumble; | ||||
|         OpusDecoder *opusDecoder; | ||||
|         OpusEncoder *opusEncoder; | ||||
| @ -48,6 +54,8 @@ namespace mumble { | ||||
|         int outgoingAudioSequenceNumber; | ||||
| 
 | ||||
|         SndfileHandle fileHandle; | ||||
| 
 | ||||
|         bool quit; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,8 @@ | ||||
| 
 | ||||
| #include <functional> | ||||
| 
 | ||||
| #include <cstring> | ||||
| 
 | ||||
| //todo wywalić
 | ||||
| #define THIS_FILE "mumsi" | ||||
| 
 | ||||
| @ -59,28 +61,24 @@ static void onCallState(pjsua_call_id call_id, | ||||
|             ci.state_text.ptr)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| pjsua::PjsuaCommunicator::PjsuaCommunicator( | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue, | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue, | ||||
| sip::PjsuaCommunicator::PjsuaCommunicator( | ||||
|         std::string host, | ||||
|         std::string user, | ||||
|         std::string password) | ||||
|         : AbstractCommunicator(inputQueue, outputQueue), | ||||
|           mediaPort(inputQueue, outputQueue) { | ||||
|         std::string password) : logger(log4cpp::Category::getInstance("SipCommunicator")), | ||||
|                                 callbackLogger(log4cpp::Category::getInstance("SipCommunicatorCallback")) { | ||||
| 
 | ||||
|     pj_status_t status; | ||||
| 
 | ||||
|     status = pjsua_create(); | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw pjsua::Exception("Error in pjsua_create()", status); | ||||
|         throw sip::Exception("Error in pjsua_create()", status); | ||||
|     } | ||||
| 
 | ||||
|     /* Init pjsua */ | ||||
|     pjsua_config generalConfig; | ||||
|     pjsua_config_default(&generalConfig); | ||||
| 
 | ||||
|     using namespace std::placeholders; | ||||
|     generalConfig.user_agent = toPjString("Mumsi Mumble-SIP Bridge"); | ||||
|     generalConfig.max_calls = 1; | ||||
| 
 | ||||
|     generalConfig.cb.on_incoming_call = &onIncomingCall; | ||||
|     generalConfig.cb.on_call_media_state = &onCallMediaState; | ||||
| @ -94,37 +92,128 @@ pjsua::PjsuaCommunicator::PjsuaCommunicator( | ||||
| 
 | ||||
|     status = pjsua_init(&generalConfig, &logConfig, NULL); | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw pjsua::Exception("Error in pjsua_init()", status); | ||||
|         throw sip::Exception("Error in pjsua_init()", status); | ||||
|     } | ||||
| 
 | ||||
|     pjsua_set_null_snd_dev(); | ||||
| 
 | ||||
|     /* Add UDP transport. */ | ||||
|     pjsua_transport_config transportConfig; | ||||
|     pjsua_transport_config_default(&transportConfig); | ||||
| 
 | ||||
|     transportConfig.port = pjsua::SIP_PORT; | ||||
|     transportConfig.port = sip::SIP_PORT; | ||||
| 
 | ||||
|     status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transportConfig, NULL); | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw pjsua::Exception("Error creating transport", status); | ||||
|         throw sip::Exception("Error creating transport", status); | ||||
|     } | ||||
| 
 | ||||
|     pjsua_set_null_snd_dev(); | ||||
| 
 | ||||
|     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", 4096, 4096, nullptr); | ||||
|     pj_pool_t *pool = pj_pool_create(&cachingPool.factory, "wav", 32768, 8192, nullptr); | ||||
| 
 | ||||
|     pjsua_conf_add_port(pool, mediaPort.create_pjmedia_port(), &mediaPortSlot); | ||||
|     // create circular buffers
 | ||||
|     pjmedia_circ_buf_create(pool, 960 * 10, &inputBuff); | ||||
|     pjmedia_circ_buf_create(pool, 960 * 10, &outputBuff); | ||||
| 
 | ||||
|     /* Initialization is done, now start pjsua */ | ||||
|     mediaPort = createMediaPort(); | ||||
| 
 | ||||
|     pjsua_conf_add_port(pool, mediaPort, &mediaPortSlot); | ||||
| 
 | ||||
|     /* Initialization is done, now start sip */ | ||||
|     status = pjsua_start(); | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw pjsua::Exception("Error starting pjsua", status); | ||||
|         throw sip::Exception("Error starting sip", status); | ||||
|     } | ||||
| 
 | ||||
|     /* Register to SIP server by creating SIP account. */ | ||||
|     pjsua_acc_config accConfig; | ||||
|     registerAccount(host, user, password); | ||||
| } | ||||
| 
 | ||||
| sip::PjsuaCommunicator::~PjsuaCommunicator() { | ||||
|     pjsua_destroy(); | ||||
| } | ||||
| 
 | ||||
| pjmedia_port *sip::PjsuaCommunicator::createMediaPort() { | ||||
| 
 | ||||
|     pjmedia_port *mp = new pjmedia_port(); | ||||
| 
 | ||||
|     pj_str_t name = toPjString("Pjsuamp"); | ||||
| 
 | ||||
|     pj_status_t status = pjmedia_port_info_init(&(mp->info), | ||||
|                                                 &name, | ||||
|                                                 PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'), | ||||
|                                                 SAMPLING_RATE, | ||||
|                                                 1, | ||||
|                                                 16, | ||||
|                                                 SAMPLING_RATE * 20 / 1000); // todo recalculate to match mumble specs
 | ||||
| 
 | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw sip::Exception("error while calling pjmedia_port_info_init().", status); | ||||
|     } | ||||
| 
 | ||||
|     mp->get_frame = &MediaPort_getFrameRawCallback; | ||||
|     mp->put_frame = &MediaPort_putFrameRawCallback; | ||||
| 
 | ||||
|     mp->port_data.pdata = this; | ||||
| 
 | ||||
|     return mp; | ||||
| } | ||||
| 
 | ||||
| pj_status_t sip::MediaPort_getFrameRawCallback(pjmedia_port *port, | ||||
|                                                pjmedia_frame *frame) { | ||||
|     PjsuaCommunicator *communicator = static_cast<PjsuaCommunicator *>(port->port_data.pdata); | ||||
|     frame->type = PJMEDIA_FRAME_TYPE_AUDIO; | ||||
| 
 | ||||
|     return communicator->mediaPortGetFrame(frame); | ||||
| } | ||||
| 
 | ||||
| pj_status_t sip::MediaPort_putFrameRawCallback(pjmedia_port *port, | ||||
|                                                pjmedia_frame *frame) { | ||||
|     PjsuaCommunicator *communicator = static_cast<PjsuaCommunicator *>(port->port_data.pdata); | ||||
|     pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf); | ||||
|     pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info); | ||||
|     frame->type = PJMEDIA_FRAME_TYPE_AUDIO; | ||||
| 
 | ||||
|     communicator->mediaPortPutFrame(samples, count); | ||||
| 
 | ||||
|     return PJ_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| pj_status_t sip::PjsuaCommunicator::mediaPortGetFrame(pjmedia_frame *frame) { | ||||
|     std::unique_lock<std::mutex> lock(inBuffAccessMutex); | ||||
| 
 | ||||
|     pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf); | ||||
|     pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&mediaPort->info); | ||||
| 
 | ||||
|     pj_size_t availableSamples = pjmedia_circ_buf_get_len(inputBuff); | ||||
|     const int samplesToRead = std::min(count, availableSamples); | ||||
| 
 | ||||
|     callbackLogger.debug("Pulling %d samples from in-buff.", samplesToRead); | ||||
|     pjmedia_circ_buf_read(inputBuff, samples, samplesToRead); | ||||
| 
 | ||||
|     if (availableSamples < count) { | ||||
|         callbackLogger.debug("Requested %d samples, available %d, filling remaining with zeros.", count, | ||||
|                              availableSamples); | ||||
|         std::memset(&(samples[availableSamples]), 0, sizeof(pj_int16_t) * (count - availableSamples)); | ||||
|     } | ||||
| 
 | ||||
|     return PJ_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
| void sip::PjsuaCommunicator::registerAccount(string host, string user, string password) { | ||||
| 
 | ||||
|     pjsua_acc_config accConfig; | ||||
|     pjsua_acc_config_default(&accConfig); | ||||
| 
 | ||||
|     accConfig.id = toPjString(string("sip:") + user + "@" + host); | ||||
| @ -138,16 +227,33 @@ pjsua::PjsuaCommunicator::PjsuaCommunicator( | ||||
|     accConfig.cred_info[0].data = toPjString(password); | ||||
| 
 | ||||
|     pjsua_acc_id acc_id; | ||||
|     status = pjsua_acc_add(&accConfig, PJ_TRUE, &acc_id); | ||||
| 
 | ||||
|     pj_status_t status = pjsua_acc_add(&accConfig, PJ_TRUE, &acc_id); | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw pjsua::Exception("Error adding account", status); | ||||
|         throw sip::Exception("failed to register account", status); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pjsua::PjsuaCommunicator::~PjsuaCommunicator() { | ||||
|     pjsua_destroy(); | ||||
| void sip::PjsuaCommunicator::pushSamples(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); | ||||
| } | ||||
| 
 | ||||
| void pjsua::PjsuaCommunicator::loop() { | ||||
| 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,33 +1,97 @@ | ||||
| #ifndef MUMSI_PJSUACOMMUNICATOR_HPP | ||||
| #define MUMSI_PJSUACOMMUNICATOR_HPP | ||||
| 
 | ||||
| #include "PjsuaMediaPort.hpp" | ||||
| #include "AbstractCommunicator.hpp" | ||||
| #include "ISamplesBuffer.hpp" | ||||
| 
 | ||||
| #include <pjmedia.h> | ||||
| 
 | ||||
| #include <string> | ||||
| #include <stdexcept> | ||||
| #include <log4cpp/Category.hh> | ||||
| #include <sndfile.hh> | ||||
| #include <mutex> | ||||
| #include <condition_variable> | ||||
| 
 | ||||
| namespace pjsua { | ||||
| namespace sip { | ||||
| 
 | ||||
|     constexpr int SIP_PORT = 5060; | ||||
|     constexpr int SAMPLING_RATE = 48000; | ||||
| 
 | ||||
|     class PjsuaCommunicator : public AbstractCommunicator { | ||||
|     class Exception : public std::runtime_error { | ||||
|     public: | ||||
|         Exception(const char *title, pj_status_t status) : std::runtime_error(title) { | ||||
|             char errorMsgBuffer[500]; | ||||
|             pj_strerror(status, errorMsgBuffer, sizeof(errorMsgBuffer)); | ||||
| 
 | ||||
|             mesg += title; | ||||
|             mesg += ": "; | ||||
|             mesg += errorMsgBuffer; | ||||
|         } | ||||
| 
 | ||||
|         virtual const char *what() const throw() { | ||||
|             return mesg.c_str(); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         std::string mesg; | ||||
|     }; | ||||
| 
 | ||||
|     inline pj_str_t toPjString(std::string str) { | ||||
|         return pj_str(const_cast<char *>(str.c_str())); | ||||
|     } | ||||
| 
 | ||||
|     pj_status_t MediaPort_getFrameRawCallback(pjmedia_port *port, pjmedia_frame *frame); | ||||
| 
 | ||||
|     pj_status_t MediaPort_putFrameRawCallback(pjmedia_port *port, pjmedia_frame *frame); | ||||
| 
 | ||||
|     class PjsuaCommunicator : public ISamplesBuffer { | ||||
|     public: | ||||
|         PjsuaCommunicator( | ||||
|                 SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue, | ||||
|                 SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue, | ||||
|                 std::string host, | ||||
|                 std::string user, | ||||
|                 std::string password); | ||||
| 
 | ||||
|         ~PjsuaCommunicator(); | ||||
| 
 | ||||
|         void loop(); | ||||
|         virtual void pushSamples(int16_t *samples, unsigned int length); | ||||
| 
 | ||||
|         virtual unsigned int pullSamples(int16_t *samples, unsigned int length, bool waitWhenEmpty); | ||||
| 
 | ||||
|     private: | ||||
|         PjsuaMediaPort mediaPort; | ||||
|         log4cpp::Category &logger; | ||||
|         log4cpp::Category &callbackLogger; | ||||
| 
 | ||||
| 
 | ||||
|         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(); | ||||
| 
 | ||||
|         void registerAccount(std::string host, | ||||
|                              std::string user, | ||||
|                              std::string password); | ||||
| 
 | ||||
|         pj_status_t mediaPortGetFrame(pjmedia_frame *frame); | ||||
| 
 | ||||
|         void mediaPortPutFrame(pj_int16_t *samples, pj_size_t count); | ||||
| 
 | ||||
|         /**
 | ||||
|         * For PJMEDIA implementation reasons, these callbacks have to be functions, not methods. | ||||
|         * That is the reason to use 'friend'. | ||||
|         */ | ||||
|         friend pj_status_t MediaPort_getFrameRawCallback(pjmedia_port *port, | ||||
|                                                          pjmedia_frame *frame); | ||||
| 
 | ||||
|         friend pj_status_t MediaPort_putFrameRawCallback(pjmedia_port *port, | ||||
|                                                          pjmedia_frame *frame); | ||||
|     }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,86 +0,0 @@ | ||||
| #include "PjsuaMediaPort.hpp" | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace pjsua; | ||||
| 
 | ||||
| 
 | ||||
| pj_status_t pjsua::MediaPort_getFrame(pjmedia_port *port, | ||||
|                                       pjmedia_frame *frame) { | ||||
|     PjsuaMediaPort *mediaPort = static_cast<PjsuaMediaPort *>(port->port_data.pdata); | ||||
|     pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf); | ||||
|     pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info); | ||||
| 
 | ||||
|     int takenSamples = mediaPort->inputQueue.pop_front(samples, count); | ||||
| 
 | ||||
|     mediaPort->fileHandle.write(samples, takenSamples); | ||||
| 
 | ||||
|     mediaPort->logger.debug("Pop %d samples from inputQueue.", takenSamples); | ||||
| 
 | ||||
|     for (int i = takenSamples; i < count; ++i) { | ||||
|         samples[i] = 0; | ||||
|     } | ||||
| 
 | ||||
|     // todo wrzucić sample do wav
 | ||||
| 
 | ||||
|     frame->type = PJMEDIA_FRAME_TYPE_AUDIO; | ||||
| 
 | ||||
|     return PJ_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| pj_status_t pjsua::MediaPort_putFrame(pjmedia_port *port, | ||||
|                                       pjmedia_frame *frame) { | ||||
|     PjsuaMediaPort *mediaPort = static_cast<PjsuaMediaPort *>(port->port_data.pdata); | ||||
|     pj_int16_t *samples = static_cast<pj_int16_t *>(frame->buf); | ||||
|     pj_size_t count = frame->size / 2 / PJMEDIA_PIA_CCNT(&port->info); | ||||
| 
 | ||||
| //    if (count > 0) {
 | ||||
| //        mediaPort->outputQueue.push_back(samples, count);
 | ||||
| //
 | ||||
| //        mediaPort->logger.debug("Push %d samples into outputQueue.", count);
 | ||||
| //    }
 | ||||
| 
 | ||||
|     return PJ_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| PjsuaMediaPort::PjsuaMediaPort( | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue, | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue) | ||||
|         : inputQueue(inputQueue), | ||||
|           outputQueue(outputQueue), | ||||
|           _pjmedia_port(nullptr), | ||||
|           logger(log4cpp::Category::getInstance("PjsuaMediaPort")) { | ||||
| 
 | ||||
|     fileHandle = SndfileHandle("capture_pjsua.wav", SFM_WRITE, SF_FORMAT_WAV | SF_FORMAT_PCM_16, 1, SAMPLING_RATE); | ||||
| } | ||||
| 
 | ||||
| pjmedia_port *pjsua::PjsuaMediaPort::create_pjmedia_port() { | ||||
| 
 | ||||
|     _pjmedia_port = new pjmedia_port(); | ||||
| 
 | ||||
|     pj_str_t name = toPjString("PjsuaMediaPort"); | ||||
| 
 | ||||
|     pj_status_t status = pjmedia_port_info_init(&(_pjmedia_port->info), | ||||
|                                                 &name, | ||||
|                                                 PJMEDIA_SIG_CLASS_PORT_AUD('s', 'i'), | ||||
|                                                 SAMPLING_RATE, | ||||
|                                                 1, | ||||
|                                                 16, | ||||
|                                                 SAMPLING_RATE * 20 / 1000); // todo recalculate to match mumble specs
 | ||||
| 
 | ||||
|     if (status != PJ_SUCCESS) { | ||||
|         throw pjsua::Exception("Error while calling pjmedia_port_info_init().", status); | ||||
|     } | ||||
| 
 | ||||
|     _pjmedia_port->get_frame = &MediaPort_getFrame; | ||||
|     _pjmedia_port->put_frame = &MediaPort_putFrame; | ||||
| 
 | ||||
|     _pjmedia_port->port_data.pdata = this; | ||||
| 
 | ||||
|     return _pjmedia_port; | ||||
| } | ||||
| 
 | ||||
| pjsua::PjsuaMediaPort::~PjsuaMediaPort() { | ||||
|     if (this->_pjmedia_port != nullptr) { | ||||
|         delete _pjmedia_port; | ||||
|     } | ||||
| } | ||||
| @ -1,68 +0,0 @@ | ||||
| #ifndef MUMSI_PJSUAMEDIAPORT_HPP | ||||
| #define MUMSI_PJSUAMEDIAPORT_HPP | ||||
| 
 | ||||
| //todo wywalić i wrzucić do nagłówka z definicjami
 | ||||
| #include "AbstractCommunicator.hpp" | ||||
| 
 | ||||
| #include <pjmedia.h> | ||||
| #include <sndfile.hh> | ||||
| 
 | ||||
| #include <log4cpp/Category.hh> | ||||
| 
 | ||||
| #include <string> | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| namespace pjsua { | ||||
| 
 | ||||
|     constexpr int SAMPLING_RATE = 48000; | ||||
| 
 | ||||
|     inline pj_str_t toPjString(std::string str) { | ||||
|         return pj_str(const_cast<char *>(str.c_str())); | ||||
|     } | ||||
| 
 | ||||
|     class Exception : public std::runtime_error { | ||||
|     public: | ||||
|         Exception(const char *title, pj_status_t status) : std::runtime_error(title) { | ||||
|             //todo status code
 | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     pj_status_t MediaPort_getFrame(pjmedia_port *port, pjmedia_frame *frame); | ||||
| 
 | ||||
|     pj_status_t MediaPort_putFrame(pjmedia_port *port, pjmedia_frame *frame); | ||||
| 
 | ||||
|     class PjsuaMediaPort { | ||||
|     public: | ||||
| 
 | ||||
|         PjsuaMediaPort( | ||||
|                 SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue, | ||||
|                 SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue); | ||||
| 
 | ||||
|         ~PjsuaMediaPort(); | ||||
| 
 | ||||
|         pjmedia_port *create_pjmedia_port(); | ||||
| 
 | ||||
| 
 | ||||
|     private: | ||||
|         log4cpp::Category &logger; | ||||
| 
 | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &inputQueue; | ||||
|         SoundSampleQueue<SOUND_SAMPLE_TYPE> &outputQueue; | ||||
| 
 | ||||
|         pjmedia_port *_pjmedia_port; | ||||
| 
 | ||||
|         SndfileHandle fileHandle; | ||||
| 
 | ||||
|         /**
 | ||||
|         * For PJSUA implementation reasons, these callbacks have to be functions, not methods. | ||||
|         * Since 'friend' usage. | ||||
|         */ | ||||
|         friend pj_status_t MediaPort_getFrame(pjmedia_port *port, | ||||
|                                               pjmedia_frame *frame); | ||||
| 
 | ||||
|         friend pj_status_t MediaPort_putFrame(pjmedia_port *port, | ||||
|                                               pjmedia_frame *frame); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif //MUMSI_PJSUAMEDIAPORT_HPP
 | ||||
| @ -1,6 +0,0 @@ | ||||
| //
 | ||||
| // Created by michal on 28.09.15.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "SoundSampleQueue.hpp" | ||||
| 
 | ||||
| @ -1,55 +0,0 @@ | ||||
| #ifndef MUMSI_SOUNDSAMPLEQUEUE_HPP | ||||
| #define MUMSI_SOUNDSAMPLEQUEUE_HPP | ||||
| 
 | ||||
| #include <mutex> | ||||
| #include <memory> | ||||
| #include <cstring> | ||||
| #include <algorithm> | ||||
| 
 | ||||
| template<typename SAMPLE_TYPE> | ||||
| class SoundSampleQueue { | ||||
| public: | ||||
|     SoundSampleQueue() | ||||
|             : start(0), | ||||
|               stop(0) { | ||||
|         buffer = new SAMPLE_TYPE[10000000]; | ||||
|     } | ||||
| 
 | ||||
|     ~SoundSampleQueue() { | ||||
|         delete[] buffer; | ||||
|     } | ||||
| 
 | ||||
|     void push_back(SAMPLE_TYPE *data, int length) { | ||||
|         std::lock_guard<std::mutex> lock(accessMutex); | ||||
| 
 | ||||
|         for (int i = 0; i < length; ++i) { | ||||
|             buffer[stop + i] = data[i]; | ||||
|         } | ||||
| 
 | ||||
|         stop += length; | ||||
|     } | ||||
| 
 | ||||
|     int pop_front(SAMPLE_TYPE *data, int maxLength) { | ||||
|         std::lock_guard<std::mutex> lock(accessMutex); | ||||
| 
 | ||||
|         int samplesToTake = std::min(stop - start, maxLength); | ||||
| 
 | ||||
|         for (int i = 0; i < samplesToTake; ++i) { | ||||
|             data[i] = buffer[start + i]; | ||||
|         } | ||||
|         start += samplesToTake; | ||||
| 
 | ||||
|         return samplesToTake; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     int start; | ||||
|     int stop; | ||||
| 
 | ||||
|     std::mutex accessMutex; | ||||
| 
 | ||||
|     SAMPLE_TYPE *buffer; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif //MUMSI_SOUNDSAMPLEQUEUE_HPP
 | ||||
| @ -1,33 +0,0 @@ | ||||
| #include "SoundSampleQueue.hpp" | ||||
| 
 | ||||
| #include <boost/test/unit_test.hpp> | ||||
| 
 | ||||
| constexpr int CHUNK = 10; | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(SoundSampleQueueTest_Run) { | ||||
| 
 | ||||
|     SoundSampleQueue<int16_t> queue; | ||||
| 
 | ||||
|     int16_t buffer[CHUNK]; | ||||
| 
 | ||||
|     int content = 0; | ||||
|     for (int j = 0; j < 10; ++j) { | ||||
|         for (int i = 0; i < CHUNK; ++i) { | ||||
|             buffer[i] = content; | ||||
|             content++; | ||||
|         } | ||||
|         queue.push_back(buffer, CHUNK); | ||||
|     } | ||||
| 
 | ||||
|     for (int j = 0; j < 12; ++j) { | ||||
|         int how_many = queue.pop_front(buffer, CHUNK); | ||||
| 
 | ||||
|         printf("%d: ", how_many); | ||||
|         for (int i = 0; i < CHUNK; ++i) { | ||||
|             printf("%d ", buffer[i]); | ||||
|         } | ||||
|         printf("\n"); | ||||
|     } | ||||
| 
 | ||||
|     BOOST_CHECK_EQUAL(1, 1); | ||||
| } | ||||
| @ -1 +1 @@ | ||||
| Subproject commit 584f3533384d7d3b47856d5ffc270f39f353040f | ||||
| Subproject commit 15ad9844680e44d32995bf8ed6858fe75fae737f | ||||
							
								
								
									
										24
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								main.cpp
									
									
									
									
									
								
							| @ -1,16 +1,10 @@ | ||||
| #include "log4cpp/Category.hh" | ||||
| #include "log4cpp/Appender.hh" | ||||
| #include "log4cpp/FileAppender.hh" | ||||
| #include "log4cpp/OstreamAppender.hh" | ||||
| #include "log4cpp/Layout.hh" | ||||
| #include "log4cpp/BasicLayout.hh" | ||||
| #include "log4cpp/Priority.hh" | ||||
| 
 | ||||
| #include "PjsuaCommunicator.hpp" | ||||
| #include "MumbleCommunicator.hpp" | ||||
| 
 | ||||
| #include "SoundSampleQueue.hpp" | ||||
| 
 | ||||
| #define SIP_DOMAIN "sip.antisip.com" | ||||
| #define SIP_USER "melangtone" | ||||
| #define SIP_PASSWD "b8DU9AZXbd9tVCWg" | ||||
| @ -24,28 +18,22 @@ int main(int argc, char *argv[]) { | ||||
|     log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout); | ||||
|     appender1->setLayout(new log4cpp::BasicLayout()); | ||||
|     log4cpp::Category &logger = log4cpp::Category::getRoot(); | ||||
|     logger.setPriority(log4cpp::Priority::DEBUG); | ||||
|     logger.setPriority(log4cpp::Priority::NOTICE); | ||||
|     logger.addAppender(appender1); | ||||
| 
 | ||||
|     SoundSampleQueue<int16_t> mumbleToSipQueue; | ||||
|     SoundSampleQueue<int16_t> sipToMumbleQueue; | ||||
| 
 | ||||
|     sip::PjsuaCommunicator pjsuaCommunicator( | ||||
|             SIP_DOMAIN, SIP_USER, SIP_PASSWD); | ||||
| 
 | ||||
|     mumble::MumbleCommunicator mumbleCommunicator( | ||||
|             sipToMumbleQueue, | ||||
|             mumbleToSipQueue, | ||||
|             pjsuaCommunicator, | ||||
|             MUMBLE_USER, MUMBLE_PASSWD, MUMBLE_DOMAIN); | ||||
| 
 | ||||
|     pjsua::PjsuaCommunicator pjsuaCommunicator( | ||||
|             mumbleToSipQueue, | ||||
|             sipToMumbleQueue, | ||||
|             SIP_DOMAIN, SIP_USER, SIP_PASSWD); | ||||
| 
 | ||||
|     logger.info("Application started."); | ||||
| 
 | ||||
|     pjsuaCommunicator.loop(); | ||||
| 
 | ||||
|     mumbleCommunicator.loop(); | ||||
| 
 | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +0,0 @@ | ||||
| #define BOOST_TEST_MODULE SZARKServerTest | ||||
| #define BOOST_TEST_DYN_LINK | ||||
| #define BOOST_TEST_MAIN | ||||
| 
 | ||||
| #include <boost/test/unit_test.hpp> | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Michał Słomkowski
						Michał Słomkowski