From 24e8ec496476196bfd925470442cbb7fdd7d9294 Mon Sep 17 00:00:00 2001 From: Auzan Date: Tue, 20 Mar 2018 11:09:25 +0700 Subject: [PATCH 1/9] adding feature sendUserState and voice target for whisper. --- .gitignore | 3 ++ include/mumlib.hpp | 8 +++++ include/mumlib/enums.hpp | 10 ++++++ src/CryptState.cpp | 3 +- src/mumlib.cpp | 69 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 90 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 4286771..da3351e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ build/ # IntelliJ *.iml .idea/ + +# Boost +include/boost/ diff --git a/include/mumlib.hpp b/include/mumlib.hpp index 95afe02..ef84744 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -50,10 +50,18 @@ namespace mumlib { void sendAudioData(int16_t *pcmData, int pcmLength); + void sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength); + void sendTextMessage(std::string message); void joinChannel(int channelId); + void sendVoiceTarget(int targetId, int channelId); + + void sendUserState(mumlib::UserState state, bool val); + + void sendUserState(mumlib::UserState state, std::string value); + private: _Mumlib_Private *impl; }; diff --git a/include/mumlib/enums.hpp b/include/mumlib/enums.hpp index b4a9b43..ea189f9 100644 --- a/include/mumlib/enums.hpp +++ b/include/mumlib/enums.hpp @@ -45,4 +45,14 @@ namespace mumlib { OPUS }; + enum class UserState { + MUTE, + DEAF, + SUPPRESS, + SELF_MUTE, + SELF_DEAF, + COMMENT, + PRIORITY_SPEAKER, + RECORDING + }; } \ No newline at end of file diff --git a/src/CryptState.cpp b/src/CryptState.cpp index 0760374..467955d 100644 --- a/src/CryptState.cpp +++ b/src/CryptState.cpp @@ -181,7 +181,8 @@ bool mumlib::CryptState::decrypt(const unsigned char *source, unsigned char *dst #define SHIFTBITS 63 typedef uint64_t subblock; -#define SWAP64(x) ({register uint64_t __out, __in = (x); __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); __out;}) +// #define SWAP64(x) ({register uint64_t __out, __in = (x); __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); __out;}) +#define SWAP64(x) (__builtin_bswap64(x)) #define SWAPPED(x) SWAP64(x) typedef subblock keyblock[BLOCKSIZE]; diff --git a/src/mumlib.cpp b/src/mumlib.cpp index 08197d1..16e5c4b 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -152,7 +152,7 @@ namespace mumlib { links_remove.push_back(channelState.links_remove(i)); } - this->channelId = channel_id; + // this->channelId = channel_id; callback.channelState( channelState.name(), @@ -369,7 +369,7 @@ namespace mumlib { void Mumlib::disconnect() { if (not impl->externalIoService) { - impl->ioService.reset(); + impl->ioService.stop(); } if (impl->transport.getConnectionState() != ConnectionState::NOT_CONNECTED) { impl->transport.disconnect(); @@ -390,6 +390,12 @@ namespace mumlib { impl->transport.sendEncodedAudioPacket(encodedData, length); } + void Mumlib::sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength) { + uint8_t encodedData[5000]; + int length = impl->audio.encodeAudioPacket(targetId, pcmData, pcmLength, encodedData, 5000); + impl->transport.sendEncodedAudioPacket(encodedData, length); + } + void Mumlib::sendTextMessage(string message) { MumbleProto::TextMessage textMessage; textMessage.set_actor(impl->sessionId); @@ -404,4 +410,63 @@ namespace mumlib { impl->transport.sendControlMessage(MessageType::USERSTATE, userState); impl->channelId = channelId; } + + void Mumlib::sendVoiceTarget(int targetId, int channelId) { + MumbleProto::VoiceTarget voiceTarget; + MumbleProto::VoiceTarget_Target voiceTargetTarget; + voiceTargetTarget.set_channel_id(channelId); + voiceTargetTarget.set_children(true); + voiceTarget.set_id(targetId); + voiceTarget.add_targets()->CopyFrom(voiceTargetTarget); + impl->transport.sendControlMessage(MessageType::VOICETARGET, voiceTarget); + } + + void Mumlib::sendUserState(mumlib::UserState field, bool val) { + MumbleProto::UserState userState; + + switch (field) { + case UserState::MUTE: + userState.set_mute(val); + break; + case UserState::DEAF: + userState.set_deaf(val); + break; + case UserState::SUPPRESS: + userState.set_suppress(val); + break; + case UserState::SELF_MUTE: + userState.set_self_mute(val); + break; + case UserState::SELF_DEAF: + userState.set_self_deaf(val); + break; + case UserState::PRIORITY_SPEAKER: + userState.set_priority_speaker(val); + break; + case UserState::RECORDING: + userState.set_recording(val); + break; + default: + // in any other case, just ignore the command + return; + } + + impl->transport.sendControlMessage(MessageType::USERSTATE, userState); + } + + void Mumlib::sendUserState(mumlib::UserState field, std::string val) { + MumbleProto::UserState userState; + + switch (field) { + case UserState::COMMENT: + // TODO: if comment longer than 128 bytes, we need to set the SHA1 hash + userState.set_comment(val); + break; + default: + // in any other case, just ignore the command + return; + } + + impl->transport.sendControlMessage(MessageType::USERSTATE, userState); + } } From 993ea209c7ff108aa3c8f632bcb33f0171d74ce7 Mon Sep 17 00:00:00 2001 From: Auzan Date: Wed, 21 Mar 2018 10:46:07 +0700 Subject: [PATCH 2/9] remove boost include library from .gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index da3351e..4286771 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,3 @@ build/ # IntelliJ *.iml .idea/ - -# Boost -include/boost/ From 7d64b05e3cb6627116d8725aec3c91694627d382 Mon Sep 17 00:00:00 2001 From: Auzan Date: Wed, 28 Mar 2018 10:37:59 +0700 Subject: [PATCH 3/9] intelij hack --- include/mumlib.hpp | 8 ++++++- include/mumlib/Audio.hpp | 11 ++++----- include/mumlib/Callback.hpp | 38 ++++++++++++++--------------- include/mumlib/Transport.hpp | 5 +++- include/mumlib/VarInt.hpp | 2 +- src/Audio.cpp | 17 ++++++------- src/Callback.cpp | 2 +- src/Transport.cpp | 46 ++++++++++++++++++++++-------------- src/VarInt.cpp | 41 ++++++++++++++++---------------- src/mumlib.cpp | 16 ++++++++++++- 10 files changed, 110 insertions(+), 76 deletions(-) diff --git a/include/mumlib.hpp b/include/mumlib.hpp index ef84744..af979a0 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -11,6 +11,7 @@ namespace mumlib { constexpr int DEFAULT_OPUS_ENCODER_BITRATE = 16000; + constexpr int DEFAULT_OPUS_SAMPLE_RATE = 48000; using namespace std; using namespace boost::asio; @@ -22,6 +23,7 @@ namespace mumlib { struct MumlibConfiguration { int opusEncoderBitrate = DEFAULT_OPUS_ENCODER_BITRATE; + int opusSampleRate = DEFAULT_OPUS_SAMPLE_RATE; // additional fields will be added in the future }; @@ -30,7 +32,7 @@ namespace mumlib { class Mumlib : boost::noncopyable { public: - Mumlib(Callback &callback); + explicit Mumlib(Callback &callback); Mumlib(Callback &callback, io_service &ioService); @@ -44,10 +46,14 @@ namespace mumlib { void disconnect(); + void reconnect(); + void run(); ConnectionState getConnectionState(); + int getChannelId(); + void sendAudioData(int16_t *pcmData, int pcmLength); void sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength); diff --git a/include/mumlib/Audio.hpp b/include/mumlib/Audio.hpp index d98f41a..df3ed11 100644 --- a/include/mumlib/Audio.hpp +++ b/include/mumlib/Audio.hpp @@ -2,19 +2,17 @@ #include "Transport.hpp" -#include +#include #include namespace mumlib { - constexpr int SAMPLE_RATE = 48000; - class MumlibException; class AudioException : public MumlibException { public: - AudioException(string message) : MumlibException(message) { } + explicit AudioException(string message) : MumlibException(message) { } }; struct IncomingAudioPacket { @@ -27,8 +25,8 @@ namespace mumlib { }; class Audio : boost::noncopyable { - public: - Audio(int opusEncoderBitrate = DEFAULT_OPUS_ENCODER_BITRATE); + public: + Audio(int opusSampleRate, int opusEncoderBitrate); virtual ~Audio(); @@ -59,6 +57,7 @@ namespace mumlib { OpusEncoder *opusEncoder; int64_t outgoingSequenceNumber; + int sampleRate; std::chrono::time_point lastEncodedAudioPacketTimestamp; }; diff --git a/include/mumlib/Callback.hpp b/include/mumlib/Callback.hpp index a11b7b4..3e3ac4c 100644 --- a/include/mumlib/Callback.hpp +++ b/include/mumlib/Callback.hpp @@ -153,7 +153,7 @@ namespace mumlib { ~BasicCallback(); - virtual void version( + void version( uint16_t major, uint8_t minor, uint8_t patch, @@ -161,29 +161,29 @@ namespace mumlib { string os, string os_version) override; - virtual void audio( + void audio( int target, int sessionId, int sequenceNumber, int16_t *pcm_data, uint32_t pcm_data_size) override; - virtual void unsupportedAudio( + void unsupportedAudio( int target, int sessionId, int sequenceNumber, uint8_t *encoded_audio_data, uint32_t encoded_audio_data_size) override; - virtual void serverSync( + void serverSync( string welcome_text, int32_t session, int32_t max_bandwidth, int64_t permissions) override; - virtual void channelRemove(uint32_t channel_id) override; + void channelRemove(uint32_t channel_id) override; - virtual void channelState( + void channelState( string name, int32_t channel_id, int32_t parent, @@ -194,13 +194,13 @@ namespace mumlib { bool temporary, int32_t position) override; - virtual void userRemove( + void userRemove( uint32_t session, int32_t actor, string reason, bool ban) override; - virtual void userState( + void userState( int32_t session, int32_t actor, string name, @@ -215,7 +215,7 @@ namespace mumlib { int32_t priority_speaker, int32_t recording) override; - virtual void banList( + void banList( const uint8_t *ip_data, uint32_t ip_data_size, uint32_t mask, @@ -225,14 +225,14 @@ namespace mumlib { string start, int32_t duration) override; - virtual void textMessage( + void textMessage( uint32_t actor, std::vector session, std::vector channel_id, std::vector tree_id, string message) override; - virtual void permissionDenied( + void permissionDenied( int32_t permission, int32_t channel_id, int32_t session, @@ -240,48 +240,48 @@ namespace mumlib { int32_t deny_type, string name) override; - virtual void queryUsers( + void queryUsers( uint32_t n_ids, uint32_t *ids, uint32_t n_names, string *names) override; - virtual void contextActionModify( + void contextActionModify( string action, string text, uint32_t m_context, uint32_t operation) override; - virtual void contextAction( + void contextAction( int32_t session, int32_t channel_id, string action) override; - virtual void userList( + void userList( uint32_t user_id, string name, string last_seen, int32_t last_channel) override; - virtual void permissionQuery( + void permissionQuery( int32_t channel_id, uint32_t permissions, int32_t flush) override; - virtual void codecVersion( + void codecVersion( int32_t alpha, int32_t beta, uint32_t prefer_alpha, int32_t opus) override; - virtual void serverConfig( + void serverConfig( uint32_t max_bandwidth, string welcome_text, uint32_t allow_html, uint32_t message_length, uint32_t image_message_length) override; - virtual void suggestConfig( + void suggestConfig( uint32_t version, uint32_t positional, uint32_t push_to_talk) override; diff --git a/include/mumlib/Transport.hpp b/include/mumlib/Transport.hpp index ccf94a7..f200dca 100644 --- a/include/mumlib/Transport.hpp +++ b/include/mumlib/Transport.hpp @@ -14,6 +14,7 @@ #include #include +#include namespace mumlib { @@ -30,7 +31,7 @@ namespace mumlib { class TransportException : public MumlibException { public: - TransportException(string message) : MumlibException(message) { } + TransportException(string message) : MumlibException(std::move(message)) { } }; class Transport : boost::noncopyable { @@ -49,6 +50,8 @@ namespace mumlib { void disconnect(); + void reconnect(); + ConnectionState getConnectionState() { return state; } diff --git a/include/mumlib/VarInt.hpp b/include/mumlib/VarInt.hpp index 451c61e..5ebacce 100644 --- a/include/mumlib/VarInt.hpp +++ b/include/mumlib/VarInt.hpp @@ -29,6 +29,6 @@ namespace mumlib { private: const int64_t value; - int64_t parseVariant(uint8_t *buffer); + long parseVariant(const uint8_t *buffer); }; } \ No newline at end of file diff --git a/src/Audio.cpp b/src/Audio.cpp index 3ce7594..384942d 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -4,20 +4,21 @@ static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5); -mumlib::Audio::Audio(int opusEncoderBitrate) +mumlib::Audio::Audio(int opusSampleRate, int opusEncoderBitrate) : logger(log4cpp::Category::getInstance("mumlib.Audio")), opusDecoder(nullptr), opusEncoder(nullptr), outgoingSequenceNumber(0) { int error; + this->sampleRate = opusSampleRate; - opusDecoder = opus_decoder_create(SAMPLE_RATE, 1, &error); + opusDecoder = opus_decoder_create(opusSampleRate, 1, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str()); } - opusEncoder = opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &error); + opusEncoder = opus_encoder_create(opusSampleRate, 1, OPUS_APPLICATION_VOIP, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str()); } @@ -108,7 +109,7 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in std::vector header; - header.push_back(0x80 | target); + header.push_back(static_cast(0x80 | target)); auto sequenceNumberEnc = VarInt(outgoingSequenceNumber).getEncoded(); header.insert(header.end(), sequenceNumberEnc.begin(), sequenceNumberEnc.end()); @@ -130,15 +131,15 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in header.insert(header.end(), outputSizeEnc.begin(), outputSizeEnc.end()); memcpy(outputBuffer, &header[0], header.size()); - memcpy(outputBuffer + header.size(), tmpOpusBuffer, outputSize); + memcpy(outputBuffer + header.size(), tmpOpusBuffer, (size_t) outputSize); - int incrementNumber = 100 * inputLength / SAMPLE_RATE; + int incrementNumber = 100 * inputLength / this->sampleRate; outgoingSequenceNumber += incrementNumber; lastEncodedAudioPacketTimestamp = std::chrono::system_clock::now(); - return outputSize + header.size(); + return static_cast(outputSize + header.size()); } void mumlib::Audio::resetEncoder() { @@ -152,7 +153,7 @@ void mumlib::Audio::resetEncoder() { } mumlib::IncomingAudioPacket mumlib::Audio::decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength) { - mumlib::IncomingAudioPacket incomingAudioPacket; + mumlib::IncomingAudioPacket incomingAudioPacket{}; incomingAudioPacket.type = static_cast((inputBuffer[0] & 0xE0) >> 5); incomingAudioPacket.target = inputBuffer[0] & 0x1F; diff --git a/src/Callback.cpp b/src/Callback.cpp index ea8fb80..5bc69e3 100644 --- a/src/Callback.cpp +++ b/src/Callback.cpp @@ -35,7 +35,7 @@ void mumlib::BasicCallback::version( os_version.c_str()); } -void mumlib::BasicCallback::audio( +void BasicCallback::audio( int target, int sessionId, int sequenceNumber, diff --git a/src/Transport.cpp b/src/Transport.cpp index f134512..e016f50 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -3,6 +3,7 @@ #include "Mumble.pb.h" #include +#include using namespace std; @@ -32,15 +33,15 @@ mumlib::Transport::Transport( bool noUdp) : logger(log4cpp::Category::getInstance("mumlib.Transport")), ioService(ioService), - processMessageFunction(processMessageFunc), - processEncodedAudioPacketFunction(processEncodedAudioPacketFunction), + processMessageFunction(std::move(processMessageFunc)), + processEncodedAudioPacketFunction(std::move(processEncodedAudioPacketFunction)), noUdp(noUdp), state(ConnectionState::NOT_CONNECTED), udpSocket(ioService), sslContext(ssl::context::sslv23), sslSocket(ioService, sslContext), pingTimer(ioService, PING_INTERVAL), - asyncBufferPool(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH)) { + asyncBufferPool(static_cast(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH))) { sslIncomingBuffer = new uint8_t[MAX_TCP_LENGTH]; @@ -118,6 +119,15 @@ void mumlib::Transport::disconnect() { } } +void mumlib::Transport::reconnect() { + boost::system::error_code errorCode; + + udpSocket.close(errorCode); + if (errorCode) { + logger.warn("SSL socket close return an error: %s.", errorCode.message().c_str()); + } +} + void mumlib::Transport::sendVersion() { MumbleProto::Version version; @@ -185,10 +195,10 @@ void mumlib::Transport::doReceiveUdp() { } uint8_t plainBuffer[1024]; - const int plainBufferLength = bytesTransferred - 4; + const int plainBufferLength = static_cast(bytesTransferred - 4); bool success = cryptState.decrypt( - udpIncomingBuffer, plainBuffer, bytesTransferred); + udpIncomingBuffer, plainBuffer, static_cast(bytesTransferred)); if (not success) { throwTransportException("UDP packet decryption failed"); @@ -263,12 +273,12 @@ void mumlib::Transport::sendUdpAsync(uint8_t *buff, int length) { auto *encryptedMsgBuff = asyncBufferPool.malloc(); const int encryptedMsgLength = length + 4; - cryptState.encrypt(buff, reinterpret_cast(encryptedMsgBuff), length); + cryptState.encrypt(buff, reinterpret_cast(encryptedMsgBuff), static_cast(length)); logger.debug("Sending %d B of data UDP asynchronously.", encryptedMsgLength); udpSocket.async_send_to( - boost::asio::buffer(encryptedMsgBuff, length + 4), + boost::asio::buffer(encryptedMsgBuff, static_cast(length + 4)), udpReceiverEndpoint, [this, encryptedMsgBuff](const boost::system::error_code &ec, size_t bytesTransferred) { asyncBufferPool.free(encryptedMsgBuff); @@ -318,7 +328,7 @@ void mumlib::Transport::doReceiveSsl() { processMessageInternal( static_cast(messageType), &sslIncomingBuffer[6], - bytesTransferred - 6); + static_cast(bytesTransferred - 6)); doReceiveSsl(); } else { @@ -438,10 +448,10 @@ void mumlib::Transport::sendUdpPing() { vector message; message.push_back(0x20); - auto timestampVarint = VarInt(time(nullptr)).getEncoded(); + auto timestampVarint = VarInt(static_cast(time(nullptr))).getEncoded(); message.insert(message.end(), timestampVarint.begin(), timestampVarint.end()); - sendUdpAsync(&message[0], message.size()); + sendUdpAsync(&message[0], static_cast(message.size())); } void mumlib::Transport::sendSsl(uint8_t *buff, int length) { @@ -453,7 +463,7 @@ void mumlib::Transport::sendSsl(uint8_t *buff, int length) { logger.debug("Sending %d bytes of data.", length); try { - write(sslSocket, boost::asio::buffer(buff, length)); + write(sslSocket, boost::asio::buffer(buff, static_cast(length))); } catch (boost::system::system_error &err) { throwTransportException(std::string("SSL send failed: ") + err.what()); } @@ -467,13 +477,13 @@ void mumlib::Transport::sendSslAsync(uint8_t *buff, int length) { auto *asyncBuff = asyncBufferPool.malloc(); - memcpy(asyncBuff, buff, length); + memcpy(asyncBuff, buff, static_cast(length)); logger.debug("Sending %d B of data asynchronously.", length); async_write( sslSocket, - boost::asio::buffer(asyncBuff, length), + boost::asio::buffer(asyncBuff, static_cast(length)), [this, asyncBuff](const boost::system::error_code &ec, size_t bytesTransferred) { asyncBufferPool.free(asyncBuff); logger.debug("Sent %d B.", bytesTransferred); @@ -499,7 +509,7 @@ void mumlib::Transport::sendControlMessagePrivate(MessageType type, google::prot const uint16_t type_network = htons(static_cast(type)); const int size = message.ByteSize(); - const uint32_t size_network = htonl(size); + const uint32_t size_network = htonl((uint32_t) size); const int length = sizeof(type_network) + sizeof(size_network) + size; @@ -517,7 +527,7 @@ void mumlib::Transport::sendControlMessagePrivate(MessageType type, google::prot void mumlib::Transport::throwTransportException(string message) { state = ConnectionState::FAILED; - throw TransportException(message); + throw TransportException(std::move(message)); } void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) { @@ -534,7 +544,7 @@ void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) { const uint16_t netUdptunnelType = htons(static_cast(MessageType::UDPTUNNEL)); - const uint32_t netLength = htonl(length); + const uint32_t netLength = htonl(static_cast(length)); const int packet = sizeof(netUdptunnelType) + sizeof(netLength) + length; @@ -542,14 +552,14 @@ void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) { memcpy(packetBuff, &netUdptunnelType, sizeof(netUdptunnelType)); memcpy(packetBuff + sizeof(netUdptunnelType), &netLength, sizeof(netLength)); - memcpy(packetBuff + sizeof(netUdptunnelType) + sizeof(netLength), buffer, length); + memcpy(packetBuff + sizeof(netUdptunnelType) + sizeof(netLength), buffer, static_cast(length)); sendSslAsync(packetBuff, length + sizeof(netUdptunnelType) + sizeof(netLength)); } } void mumlib::Transport::processAudioPacket(uint8_t *buff, int length) { - AudioPacketType type = static_cast((buff[0] & 0xE0) >> 5); + auto type = static_cast((buff[0] & 0xE0) >> 5); switch (type) { case AudioPacketType::CELT_Alpha: case AudioPacketType::Speex: diff --git a/src/VarInt.cpp b/src/VarInt.cpp index 2eeebf8..bd489da 100644 --- a/src/VarInt.cpp +++ b/src/VarInt.cpp @@ -12,7 +12,7 @@ mumlib::VarInt::VarInt(std::vector encoded) : value(parseVariant(&encod * This code was taken from Mumble source code * https://github.com/mumble-voip/mumble/blob/master/src/PacketDataStream.h */ -int64_t mumlib::VarInt::parseVariant(uint8_t *buffer) { +int64_t mumlib::VarInt::parseVariant(const uint8_t *buffer) { int64_t v = buffer[0]; if ((v & 0x80) == 0x00) { return (v & 0x7F); @@ -36,6 +36,7 @@ int64_t mumlib::VarInt::parseVariant(uint8_t *buffer) { return (v & 0x1F) << 16 | buffer[1] << 8 | buffer[2]; } + throw VarIntException("invalid varint"); } @@ -46,7 +47,7 @@ std::vector mumlib::VarInt::getEncoded() const { if ((i & 0x8000000000000000LL) && (~i < 0x100000000LL)) { i = ~i; if (i <= 0x3) { - encoded.push_back(0xFC | i); + encoded.push_back(static_cast(0xFC | i)); return encoded; } else { encoded.push_back(0xF8); @@ -54,29 +55,29 @@ std::vector mumlib::VarInt::getEncoded() const { } if (i < 0x80) { - encoded.push_back(i); + encoded.push_back(static_cast(i)); } else if (i < 0x4000) { - encoded.push_back(0x80 | (i >> 8)); - encoded.push_back(i & 0xFF); + encoded.push_back(static_cast(0x80 | (i >> 8))); + encoded.push_back(static_cast(i & 0xFF)); } else if (i < 0x200000) { - encoded.push_back(0xC0 | (i >> 16)); - encoded.push_back((i >> 8) & 0xFF); - encoded.push_back(i & 0xFF); + encoded.push_back(static_cast(0xC0 | (i >> 16))); + encoded.push_back(static_cast((i >> 8) & 0xFF)); + encoded.push_back(static_cast(i & 0xFF)); } else if (i < 0x10000000) { - encoded.push_back(0xE0 | (i >> 24)); - encoded.push_back((i >> 16) & 0xFF); - encoded.push_back((i >> 8) & 0xFF); - encoded.push_back(i & 0xFF); + encoded.push_back(static_cast(0xE0 | (i >> 24))); + encoded.push_back(static_cast((i >> 16) & 0xFF)); + encoded.push_back(static_cast((i >> 8) & 0xFF)); + encoded.push_back(static_cast(i & 0xFF)); } else { encoded.push_back(0xF4); - encoded.push_back((i >> 56) & 0xFF); - encoded.push_back((i >> 48) & 0xFF); - encoded.push_back((i >> 40) & 0xFF); - encoded.push_back((i >> 32) & 0xFF); - encoded.push_back((i >> 24) & 0xFF); - encoded.push_back((i >> 16) & 0xFF); - encoded.push_back((i >> 8) & 0xFF); - encoded.push_back(i & 0xFF); + encoded.push_back(static_cast((i >> 56) & 0xFF)); + encoded.push_back(static_cast((i >> 48) & 0xFF)); + encoded.push_back(static_cast((i >> 40) & 0xFF)); + encoded.push_back(static_cast((i >> 32) & 0xFF)); + encoded.push_back(static_cast((i >> 24) & 0xFF)); + encoded.push_back(static_cast((i >> 16) & 0xFF)); + encoded.push_back(static_cast((i >> 8) & 0xFF)); + encoded.push_back(static_cast(i & 0xFF)); } return encoded; diff --git a/src/mumlib.cpp b/src/mumlib.cpp index 16e5c4b..6f7038f 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -43,7 +43,8 @@ namespace mumlib { ioService(ioService), externalIoService(true), transport(ioService, boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3), - boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3)) { + boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3)), + audio(configuration.opusSampleRate, configuration.opusEncoderBitrate) { audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate); } @@ -363,6 +364,10 @@ namespace mumlib { return impl->transport.getConnectionState(); } + int Mumlib::getChannelId() { + return impl->channelId; + } + void Mumlib::connect(string host, int port, string user, string password) { impl->transport.connect(host, port, user, password); } @@ -376,6 +381,15 @@ namespace mumlib { } } + void Mumlib::reconnect() { + if (not impl->externalIoService) { + impl->ioService.reset(); + } + if (impl->transport.getConnectionState() != ConnectionState::NOT_CONNECTED) { + impl->transport.disconnect(); + } + } + void Mumlib::run() { if (impl->externalIoService) { throw MumlibException("can't call run() when using external io_service"); From 429e1a54f7a0ea8604a172f78a615e86f73c5c3f Mon Sep 17 00:00:00 2001 From: Auzan Date: Thu, 29 Mar 2018 09:53:47 +0700 Subject: [PATCH 4/9] adding opus chanel in mumblib configuration --- include/mumlib.hpp | 2 ++ include/mumlib/Audio.hpp | 6 ++++-- include/mumlib/VarInt.hpp | 2 +- src/Audio.cpp | 6 +++--- src/Transport.cpp | 1 + src/mumlib.cpp | 2 +- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/mumlib.hpp b/include/mumlib.hpp index af979a0..da0baf0 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -12,6 +12,7 @@ namespace mumlib { constexpr int DEFAULT_OPUS_ENCODER_BITRATE = 16000; constexpr int DEFAULT_OPUS_SAMPLE_RATE = 48000; + constexpr int DEFAULT_OPUS_NUM_CHANNELS = 1; using namespace std; using namespace boost::asio; @@ -24,6 +25,7 @@ namespace mumlib { struct MumlibConfiguration { int opusEncoderBitrate = DEFAULT_OPUS_ENCODER_BITRATE; int opusSampleRate = DEFAULT_OPUS_SAMPLE_RATE; + int opusChannels = DEFAULT_OPUS_NUM_CHANNELS; // additional fields will be added in the future }; diff --git a/include/mumlib/Audio.hpp b/include/mumlib/Audio.hpp index df3ed11..7e1433a 100644 --- a/include/mumlib/Audio.hpp +++ b/include/mumlib/Audio.hpp @@ -25,8 +25,10 @@ namespace mumlib { }; class Audio : boost::noncopyable { - public: - Audio(int opusSampleRate, int opusEncoderBitrate); + public: + explicit Audio(int opusSampleRate=DEFAULT_OPUS_SAMPLE_RATE, + int opusEncoderBitrate=DEFAULT_OPUS_ENCODER_BITRATE, + int channels=DEFAULT_OPUS_NUM_CHANNELS); virtual ~Audio(); diff --git a/include/mumlib/VarInt.hpp b/include/mumlib/VarInt.hpp index 5ebacce..7e301c7 100644 --- a/include/mumlib/VarInt.hpp +++ b/include/mumlib/VarInt.hpp @@ -29,6 +29,6 @@ namespace mumlib { private: const int64_t value; - long parseVariant(const uint8_t *buffer); + int64_t parseVariant(const uint8_t *buffer); }; } \ No newline at end of file diff --git a/src/Audio.cpp b/src/Audio.cpp index 384942d..2a0eac2 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -4,7 +4,7 @@ static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5); -mumlib::Audio::Audio(int opusSampleRate, int opusEncoderBitrate) +mumlib::Audio::Audio(int opusSampleRate, int opusEncoderBitrate, int channels) : logger(log4cpp::Category::getInstance("mumlib.Audio")), opusDecoder(nullptr), opusEncoder(nullptr), @@ -13,12 +13,12 @@ mumlib::Audio::Audio(int opusSampleRate, int opusEncoderBitrate) int error; this->sampleRate = opusSampleRate; - opusDecoder = opus_decoder_create(opusSampleRate, 1, &error); + opusDecoder = opus_decoder_create(opusSampleRate, channels, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str()); } - opusEncoder = opus_encoder_create(opusSampleRate, 1, OPUS_APPLICATION_VOIP, &error); + opusEncoder = opus_encoder_create(opusSampleRate, channels, OPUS_APPLICATION_VOIP, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str()); } diff --git a/src/Transport.cpp b/src/Transport.cpp index e016f50..2c14e4d 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -95,6 +95,7 @@ void mumlib::Transport::connect( void mumlib::Transport::disconnect() { + pingTimer.cancel(); if (state != ConnectionState::NOT_CONNECTED) { boost::system::error_code errorCode; diff --git a/src/mumlib.cpp b/src/mumlib.cpp index 6f7038f..f289992 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -44,7 +44,7 @@ namespace mumlib { externalIoService(true), transport(ioService, boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3), boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3)), - audio(configuration.opusSampleRate, configuration.opusEncoderBitrate) { + audio(configuration.opusSampleRate, configuration.opusEncoderBitrate, configuration.opusChannels) { audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate); } From 38f480566085f01d9776dc69b44db6778a0d47f4 Mon Sep 17 00:00:00 2001 From: Auzan Date: Thu, 12 Apr 2018 08:33:44 +0700 Subject: [PATCH 5/9] Update mumble proto to latest version. --- Mumble.proto | 35 +++++++++++++++++++++++++++-------- include/mumlib.hpp | 2 -- include/mumlib/Transport.hpp | 2 -- src/Transport.cpp | 11 ----------- src/mumlib.cpp | 32 +++++++++++++++++++++----------- 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/Mumble.proto b/Mumble.proto index 92b8737..a74f247 100644 --- a/Mumble.proto +++ b/Mumble.proto @@ -1,3 +1,10 @@ +// Copyright 2005-2018 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +syntax = "proto2"; + package MumbleProto; option optimize_for = SPEED; @@ -63,7 +70,8 @@ message Ping { // Sent by the server when it rejects the user connection. message Reject { enum RejectType { - // TODO ?? + // The rejection reason is unknown (details should be available + // in Reject.reason). None = 0; // The client attempted to connect with an incompatible version. WrongVersion = 1; @@ -98,7 +106,7 @@ message ServerSync { optional uint32 max_bandwidth = 2; // Server welcome text. optional string welcome_text = 3; - // Current user permissions TODO: Confirm?? + // Current user permissions in the root channel. optional uint64 permissions = 4; } @@ -134,6 +142,10 @@ message ChannelState { optional int32 position = 9 [default = 0]; // SHA1 hash of the description if the description is 128 bytes or more. optional bytes description_hash = 10; + // Maximum number of users allowed in the channel. If this value is zero, + // the maximum number of users allowed in the channel is given by the + // server's "usersperchannel" setting. + optional uint32 max_users = 11; } // Used to communicate user leaving or being kicked. May be sent by the client @@ -180,9 +192,14 @@ message UserState { optional bool self_deaf = 10; // User image if it is less than 128 bytes. optional bytes texture = 11; - // TODO ?? + // The positional audio plugin identifier. + // Positional audio information is only sent to users who share + // identical plugin contexts. + // + // This value is not trasmitted to clients. optional bytes plugin_context = 12; - // TODO ?? + // The user's plugin-specific identity. + // This value is not transmitted to clients. optional string plugin_identity = 13; // User comment if it is less than 128 bytes. optional string comment = 14; @@ -209,7 +226,7 @@ message BanList { required uint32 mask = 2; // User name for identification purposes (does not affect the ban). optional string name = 3; - // TODO ?? + // The certificate hash of the banned user. optional string hash = 4; // Reason for the ban (does not affect the ban). optional string reason = 5; @@ -403,9 +420,9 @@ message VoiceTarget { message Target { // Users that are included as targets. repeated uint32 session = 1; - // Channels that are included as targets. + // Channel that is included as a target. optional uint32 channel_id = 2; - // TODO ?? + // ACL group that is included as a target. optional string group = 3; // True if the voice should follow links from the specified channel. optional bool links = 4 [default = false]; @@ -529,6 +546,8 @@ message ServerConfig { optional uint32 message_length = 4; // Maximum image message length. optional uint32 image_message_length = 5; + // The maximum number of users allowed on the server. + optional uint32 max_users = 6; } // Sent by the server to inform the clients of suggested client configuration @@ -541,4 +560,4 @@ message SuggestConfig { optional bool positional = 2; // True if the administrator suggests push to talk to be used on this server. optional bool push_to_talk = 3; -} +} \ No newline at end of file diff --git a/include/mumlib.hpp b/include/mumlib.hpp index da0baf0..ee18567 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -48,8 +48,6 @@ namespace mumlib { void disconnect(); - void reconnect(); - void run(); ConnectionState getConnectionState(); diff --git a/include/mumlib/Transport.hpp b/include/mumlib/Transport.hpp index f200dca..346728b 100644 --- a/include/mumlib/Transport.hpp +++ b/include/mumlib/Transport.hpp @@ -50,8 +50,6 @@ namespace mumlib { void disconnect(); - void reconnect(); - ConnectionState getConnectionState() { return state; } diff --git a/src/Transport.cpp b/src/Transport.cpp index 2c14e4d..b3803f6 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -95,7 +95,6 @@ void mumlib::Transport::connect( void mumlib::Transport::disconnect() { - pingTimer.cancel(); if (state != ConnectionState::NOT_CONNECTED) { boost::system::error_code errorCode; @@ -120,16 +119,6 @@ void mumlib::Transport::disconnect() { } } -void mumlib::Transport::reconnect() { - boost::system::error_code errorCode; - - udpSocket.close(errorCode); - if (errorCode) { - logger.warn("SSL socket close return an error: %s.", errorCode.message().c_str()); - } -} - - void mumlib::Transport::sendVersion() { MumbleProto::Version version; diff --git a/src/mumlib.cpp b/src/mumlib.cpp index f289992..e21d1e1 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -201,6 +202,9 @@ namespace mumlib { int32_t priority_speaker = userState.has_priority_speaker() ? userState.priority_speaker() : -1; int32_t recording = userState.has_recording() ? userState.recording() : -1; + if(session == this->sessionId) { + this->channelId = channel_id; + } callback.userState(session, actor, userState.name(), @@ -381,15 +385,6 @@ namespace mumlib { } } - void Mumlib::reconnect() { - if (not impl->externalIoService) { - impl->ioService.reset(); - } - if (impl->transport.getConnectionState() != ConnectionState::NOT_CONNECTED) { - impl->transport.disconnect(); - } - } - void Mumlib::run() { if (impl->externalIoService) { throw MumlibException("can't call run() when using external io_service"); @@ -473,8 +468,23 @@ namespace mumlib { switch (field) { case UserState::COMMENT: - // TODO: if comment longer than 128 bytes, we need to set the SHA1 hash - userState.set_comment(val); + + if(val.size() < 128) { + userState.set_comment(val); + } else { + // if comment longer than 128 bytes, we need to set the SHA1 hash + boost::uuids::detail::sha1 sha1; + uint hash[5]; + sha1.process_bytes(val.c_str(), val.size()); + sha1.get_digest(hash); + + std::stringstream valStream; + for(std::size_t i=0; i Date: Wed, 18 Apr 2018 12:43:52 +0700 Subject: [PATCH 6/9] Add MumbleUser and MumbleChannel --- include/mumlib.hpp | 23 ++++++++++- include/mumlib/CryptState.hpp | 7 +++- src/CryptState.cpp | 38 +++++++++++++++++- src/Transport.cpp | 1 - src/mumlib.cpp | 74 +++++++++++++++++++++++++++++++++-- 5 files changed, 134 insertions(+), 9 deletions(-) diff --git a/include/mumlib.hpp b/include/mumlib.hpp index ee18567..96b2165 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -29,6 +29,17 @@ namespace mumlib { // additional fields will be added in the future }; + struct MumbleUser { + int32_t sessionId; + string name; + }; + + struct MumbleChannel { + int32_t channelId; + string name; + string description; + }; + struct _Mumlib_Private; @@ -54,6 +65,10 @@ namespace mumlib { int getChannelId(); + vector getListUser(); + + vector getListChannel(); + void sendAudioData(int16_t *pcmData, int pcmLength); void sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength); @@ -62,13 +77,19 @@ namespace mumlib { void joinChannel(int channelId); + void joinChannel(std::string channelName); + void sendVoiceTarget(int targetId, int channelId); + void sendVoiceTarget(int targetId, std::string channelName); + void sendUserState(mumlib::UserState state, bool val); void sendUserState(mumlib::UserState state, std::string value); - + private: _Mumlib_Private *impl; + + int getListChannelIdBy(std::string channelName); }; } diff --git a/include/mumlib/CryptState.hpp b/include/mumlib/CryptState.hpp index cf5b61f..5d53c73 100644 --- a/include/mumlib/CryptState.hpp +++ b/include/mumlib/CryptState.hpp @@ -36,7 +36,7 @@ namespace mumlib { class CryptState : boost::noncopyable { - public: + private: unsigned char raw_key[AES_BLOCK_SIZE]; unsigned char encrypt_iv[AES_BLOCK_SIZE]; unsigned char decrypt_iv[AES_BLOCK_SIZE]; @@ -56,14 +56,19 @@ namespace mumlib { AES_KEY decrypt_key; bool bInit; + public: CryptState(); bool isValid() const; + void genKey(); + void setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div); void setDecryptIV(const unsigned char *iv); + const unsigned char* getEncryptIV() const; + void ocb_encrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len, const unsigned char *nonce, unsigned char *tag); diff --git a/src/CryptState.cpp b/src/CryptState.cpp index 467955d..55ca26d 100644 --- a/src/CryptState.cpp +++ b/src/CryptState.cpp @@ -40,6 +40,7 @@ #include "mumlib/CryptState.hpp" +#include #include #include @@ -57,6 +58,15 @@ bool mumlib::CryptState::isValid() const { return bInit; } +void mumlib::CryptState::genKey() { + RAND_bytes(raw_key, AES_BLOCK_SIZE); + RAND_bytes(encrypt_iv, AES_BLOCK_SIZE); + RAND_bytes(decrypt_iv, AES_BLOCK_SIZE); + AES_set_encrypt_key(raw_key, 128, &encrypt_key); + AES_set_decrypt_key(raw_key, 128, &decrypt_key); + bInit = true; +} + void mumlib::CryptState::setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div) { memcpy(raw_key, rkey, AES_BLOCK_SIZE); memcpy(encrypt_iv, eiv, AES_BLOCK_SIZE); @@ -70,6 +80,10 @@ void mumlib::CryptState::setDecryptIV(const unsigned char *iv) { memcpy(decrypt_iv, iv, AES_BLOCK_SIZE); } +const unsigned char* mumlib::CryptState::getEncryptIV() const { + return encrypt_iv; +} + void mumlib::CryptState::encrypt(const unsigned char *source, unsigned char *dst, unsigned int plain_length) { unsigned char tag[AES_BLOCK_SIZE]; @@ -177,14 +191,34 @@ bool mumlib::CryptState::decrypt(const unsigned char *source, unsigned char *dst return true; } +#if defined(__LP64__) + #define BLOCKSIZE 2 #define SHIFTBITS 63 typedef uint64_t subblock; -// #define SWAP64(x) ({register uint64_t __out, __in = (x); __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); __out;}) -#define SWAP64(x) (__builtin_bswap64(x)) +#ifdef __x86_64__ +static inline uint64_t SWAP64(register uint64_t __in) { register uint64_t __out; __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); return __out; } +#else +#define SWAP64(x) ((static_cast(x) << 56) | \ + ((static_cast(x) << 40) & 0xff000000000000ULL) | \ + ((static_cast(x) << 24) & 0xff0000000000ULL) | \ + ((static_cast(x) << 8) & 0xff00000000ULL) | \ + ((static_cast(x) >> 8) & 0xff000000ULL) | \ + ((static_cast(x) >> 24) & 0xff0000ULL) | \ + ((static_cast(x) >> 40) & 0xff00ULL) | \ + ((static_cast(x) >> 56))) +#endif +// #define SWAP64(x) (__builtin_bswap64(x)) #define SWAPPED(x) SWAP64(x) +#else +#define BLOCKSIZE 4 +#define SHIFTBITS 31 +typedef uint32_t subblock; +#define SWAPPED(x) htonl(x) +#endif + typedef subblock keyblock[BLOCKSIZE]; static void inline XOR(subblock *dst, const subblock *a, const subblock *b) { diff --git a/src/Transport.cpp b/src/Transport.cpp index b3803f6..01b712a 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -3,7 +3,6 @@ #include "Mumble.pb.h" #include -#include using namespace std; diff --git a/src/mumlib.cpp b/src/mumlib.cpp index e21d1e1..ff3c76b 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -34,6 +34,9 @@ namespace mumlib { int sessionId = 0; int channelId = 0; + std::vector listMumbleUser; + std::vector listMumbleChannel; + _Mumlib_Private(Callback &callback, MumlibConfiguration &configuration) : _Mumlib_Private(callback, *(new io_service()), configuration) { externalIoService = false; @@ -154,7 +157,14 @@ namespace mumlib { links_remove.push_back(channelState.links_remove(i)); } - // this->channelId = channel_id; + MumbleChannel mumbleChannel; + mumbleChannel.channelId = channel_id; + mumbleChannel.name = channelState.name(); + mumbleChannel.description = channelState.description(); + + if(not isListChannelContains(channel_id)) { + listMumbleChannel.push_back(mumbleChannel); + } callback.channelState( channelState.name(), @@ -177,6 +187,10 @@ namespace mumlib { bool ban = user_remove.has_ban() ? user_remove.ban() : false; //todo make sure it's correct to assume it's false + if(isListUserContains(user_remove.session())) { + listUserRemovedBy(user_remove.session()); + } + callback.userRemove( user_remove.session(), actor, @@ -205,6 +219,15 @@ namespace mumlib { if(session == this->sessionId) { this->channelId = channel_id; } + + MumbleUser mumbleUser; + mumbleUser.name = userState.name(); + mumbleUser.sessionId = session; + + if(not isListUserContains(session)) { + listMumbleUser.push_back(mumbleUser); + } + callback.userState(session, actor, userState.name(), @@ -339,7 +362,25 @@ namespace mumlib { return true; } + bool isListUserContains(int sessionId) { + for(int i = 0; i < listMumbleUser.size(); i++) + if(listMumbleUser[i].sessionId == sessionId) + return true; + return false; + } + void listUserRemovedBy(int sessionId) { + for(int i = 0; i < listMumbleUser.size(); i++) + if(listMumbleUser[i].sessionId == sessionId) + listMumbleUser.erase(listMumbleUser.begin() + i); + } + + bool isListChannelContains(int channelId) { + for(int i = 0; i < listMumbleChannel.size(); i++) + if(listMumbleChannel[i].channelId == channelId) + return true; + return false; + } }; Mumlib::Mumlib(Callback &callback) { @@ -372,6 +413,14 @@ namespace mumlib { return impl->channelId; } + vector Mumlib::getListUser() { + return impl->listMumbleUser; + } + + vector Mumlib::getListChannel() { + return impl->listMumbleChannel; + } + void Mumlib::connect(string host, int port, string user, string password) { impl->transport.connect(host, port, user, password); } @@ -394,9 +443,7 @@ namespace mumlib { } void Mumlib::sendAudioData(int16_t *pcmData, int pcmLength) { - uint8_t encodedData[5000]; - int length = impl->audio.encodeAudioPacket(0, pcmData, pcmLength, encodedData, 5000); - impl->transport.sendEncodedAudioPacket(encodedData, length); + Mumlib::sendAudioDataTarget(0, pcmData, pcmLength); } void Mumlib::sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength) { @@ -420,6 +467,11 @@ namespace mumlib { impl->channelId = channelId; } + void Mumlib::joinChannel(string name) { + int channelId = Mumlib::getListChannelIdBy(name); + Mumlib::joinChannel(channelId); + } + void Mumlib::sendVoiceTarget(int targetId, int channelId) { MumbleProto::VoiceTarget voiceTarget; MumbleProto::VoiceTarget_Target voiceTargetTarget; @@ -430,6 +482,11 @@ namespace mumlib { impl->transport.sendControlMessage(MessageType::VOICETARGET, voiceTarget); } + void Mumlib::sendVoiceTarget(int targetId, string channelName) { + int channelId = Mumlib::getListChannelIdBy(channelName); + Mumlib::sendVoiceTarget(targetId, channelId); + } + void Mumlib::sendUserState(mumlib::UserState field, bool val) { MumbleProto::UserState userState; @@ -493,4 +550,13 @@ namespace mumlib { impl->transport.sendControlMessage(MessageType::USERSTATE, userState); } + + int Mumlib::getListChannelIdBy(string name) { + vector listMumbleChannel = impl->listMumbleChannel; + int channelId = impl->channelId; + for(int i = 0; i < listMumbleChannel.size(); i++) + if(listMumbleChannel[i].name == name) + channelId = listMumbleChannel[i].channelId; + return channelId; + } } From f6ef5949a16df3af2ae4d0f2ab6c78e83698c670 Mon Sep 17 00:00:00 2001 From: Auzan Date: Fri, 20 Apr 2018 11:43:27 +0700 Subject: [PATCH 7/9] adding some method, and look for cause issue #3 --- include/mumlib.hpp | 12 ++++--- include/mumlib/enums.hpp | 5 +++ src/Audio.cpp | 12 ++++++- src/CryptState.cpp | 23 +------------ src/mumlib.cpp | 71 +++++++++++++++++++++++++++++++++------- 5 files changed, 83 insertions(+), 40 deletions(-) diff --git a/include/mumlib.hpp b/include/mumlib.hpp index 96b2165..cab8e31 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -65,9 +65,9 @@ namespace mumlib { int getChannelId(); - vector getListUser(); + vector getListAllUser(); - vector getListChannel(); + vector getListAllChannel(); void sendAudioData(int16_t *pcmData, int pcmLength); @@ -79,9 +79,9 @@ namespace mumlib { void joinChannel(std::string channelName); - void sendVoiceTarget(int targetId, int channelId); + void sendVoiceTarget(mumlib::VoiceTargetType type, int targetId, int id); - void sendVoiceTarget(int targetId, std::string channelName); + bool sendVoiceTarget(mumlib::VoiceTargetType type, int targetId, std::string name); void sendUserState(mumlib::UserState state, bool val); @@ -90,6 +90,8 @@ namespace mumlib { private: _Mumlib_Private *impl; - int getListChannelIdBy(std::string channelName); + int getChannelIdBy(std::string channelName); + + int getUserIdBy(std::string userName); }; } diff --git a/include/mumlib/enums.hpp b/include/mumlib/enums.hpp index ea189f9..740d83b 100644 --- a/include/mumlib/enums.hpp +++ b/include/mumlib/enums.hpp @@ -55,4 +55,9 @@ namespace mumlib { PRIORITY_SPEAKER, RECORDING }; + + enum class VoiceTargetType { + CHANNEL, + USER + }; } \ No newline at end of file diff --git a/src/Audio.cpp b/src/Audio.cpp index 2a0eac2..60277df 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -76,6 +76,16 @@ std::pair mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer, % inputLength % dataPointer % opusDataLength).str()); } + // Issue #3 (Users speaking simultaneously) + // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html + // Opus is a stateful codec with overlapping blocks and as a result Opus packets are not coded independently of each other. + // Packets must be passed into the decoder serially and in the correct order for a correct decode. + // Lost packets can be replaced with loss concealment by calling the decoder with a null pointer and zero length for the missing packet. + // A single codec state may only be accessed from a single thread at a time and any required locking must be performed by the caller. + // Separate streams must be decoded with separate decoder states and can be decoded in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK defined. + + int frame = opus_packet_get_nb_frames(&inputBuffer[dataPointer], opusDataLength); + int samples = frame * opus_packet_get_samples_per_frame(&inputBuffer[dataPointer], sampleRate); int outputSize = opus_decode(opusDecoder, reinterpret_cast(&inputBuffer[dataPointer]), opusDataLength, @@ -133,7 +143,7 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in memcpy(outputBuffer, &header[0], header.size()); memcpy(outputBuffer + header.size(), tmpOpusBuffer, (size_t) outputSize); - int incrementNumber = 100 * inputLength / this->sampleRate; + int incrementNumber = 100 * inputLength / sampleRate; outgoingSequenceNumber += incrementNumber; diff --git a/src/CryptState.cpp b/src/CryptState.cpp index 55ca26d..e1763c3 100644 --- a/src/CryptState.cpp +++ b/src/CryptState.cpp @@ -191,34 +191,13 @@ bool mumlib::CryptState::decrypt(const unsigned char *source, unsigned char *dst return true; } -#if defined(__LP64__) - #define BLOCKSIZE 2 #define SHIFTBITS 63 typedef uint64_t subblock; -#ifdef __x86_64__ -static inline uint64_t SWAP64(register uint64_t __in) { register uint64_t __out; __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); return __out; } -#else -#define SWAP64(x) ((static_cast(x) << 56) | \ - ((static_cast(x) << 40) & 0xff000000000000ULL) | \ - ((static_cast(x) << 24) & 0xff0000000000ULL) | \ - ((static_cast(x) << 8) & 0xff00000000ULL) | \ - ((static_cast(x) >> 8) & 0xff000000ULL) | \ - ((static_cast(x) >> 24) & 0xff0000ULL) | \ - ((static_cast(x) >> 40) & 0xff00ULL) | \ - ((static_cast(x) >> 56))) -#endif -// #define SWAP64(x) (__builtin_bswap64(x)) +#define SWAP64(x) (__builtin_bswap64(x)) #define SWAPPED(x) SWAP64(x) -#else -#define BLOCKSIZE 4 -#define SHIFTBITS 31 -typedef uint32_t subblock; -#define SWAPPED(x) htonl(x) -#endif - typedef subblock keyblock[BLOCKSIZE]; static void inline XOR(subblock *dst, const subblock *a, const subblock *b) { diff --git a/src/mumlib.cpp b/src/mumlib.cpp index ff3c76b..c8101b5 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -65,6 +65,8 @@ namespace mumlib { auto incomingAudioPacket = audio.decodeIncomingAudioPacket(buffer, length); if (type == AudioPacketType::OPUS) { + // todo: multiple users speaking simultaneously (Issue #3) + // something weird while decoding the opus payload int16_t pcmData[5000]; auto status = audio.decodeOpusPayload(incomingAudioPacket.audioPayload, incomingAudioPacket.audioPayloadLength, @@ -127,6 +129,11 @@ namespace mumlib { case MessageType::CHANNELREMOVE: { MumbleProto::ChannelRemove channelRemove; channelRemove.ParseFromArray(buffer, length); + + if(isListChannelContains(channelRemove.channel_id())) { + listChannelRemovedBy(channelRemove.channel_id()); + } + callback.channelRemove(channelRemove.channel_id()); } break; @@ -381,6 +388,12 @@ namespace mumlib { return true; return false; } + + void listChannelRemovedBy(int channelId) { + for(int i = 0; i < listMumbleChannel.size(); i++) + if(listMumbleChannel[i].channelId == channelId) + listMumbleChannel.erase(listMumbleChannel.begin() + i); + } }; Mumlib::Mumlib(Callback &callback) { @@ -413,11 +426,11 @@ namespace mumlib { return impl->channelId; } - vector Mumlib::getListUser() { + vector Mumlib::getListAllUser() { return impl->listMumbleUser; } - vector Mumlib::getListChannel() { + vector Mumlib::getListAllChannel() { return impl->listMumbleChannel; } @@ -468,23 +481,48 @@ namespace mumlib { } void Mumlib::joinChannel(string name) { - int channelId = Mumlib::getListChannelIdBy(name); - Mumlib::joinChannel(channelId); + int channelId = Mumlib::getChannelIdBy(name); + if(!channelId < 0) // when channel has not been registered / create + Mumlib::joinChannel(channelId); } - void Mumlib::sendVoiceTarget(int targetId, int channelId) { + void Mumlib::sendVoiceTarget(VoiceTargetType type, int targetId, int id) { MumbleProto::VoiceTarget voiceTarget; MumbleProto::VoiceTarget_Target voiceTargetTarget; - voiceTargetTarget.set_channel_id(channelId); - voiceTargetTarget.set_children(true); + switch(type) { + case VoiceTargetType::CHANNEL: { + voiceTargetTarget.set_channel_id(id); + voiceTargetTarget.set_children(true); + } + break; + case VoiceTargetType::USER: { + voiceTargetTarget.add_session(id); + } + break; + default: + return; + } voiceTarget.set_id(targetId); voiceTarget.add_targets()->CopyFrom(voiceTargetTarget); impl->transport.sendControlMessage(MessageType::VOICETARGET, voiceTarget); } - void Mumlib::sendVoiceTarget(int targetId, string channelName) { - int channelId = Mumlib::getListChannelIdBy(channelName); - Mumlib::sendVoiceTarget(targetId, channelId); + bool Mumlib::sendVoiceTarget(VoiceTargetType type, int targetId, string name) { + int id = -1; + switch(type) { + case VoiceTargetType::CHANNEL: + id = getChannelIdBy(name); + break; + case VoiceTargetType::USER: + id = getUserIdBy(name); + break; + default: + break; + } + if(id < 0) + return false; + sendVoiceTarget(type, targetId, id); + return true; } void Mumlib::sendUserState(mumlib::UserState field, bool val) { @@ -551,12 +589,21 @@ namespace mumlib { impl->transport.sendControlMessage(MessageType::USERSTATE, userState); } - int Mumlib::getListChannelIdBy(string name) { + int Mumlib::getChannelIdBy(string name) { vector listMumbleChannel = impl->listMumbleChannel; - int channelId = impl->channelId; + int channelId = -1; for(int i = 0; i < listMumbleChannel.size(); i++) if(listMumbleChannel[i].name == name) channelId = listMumbleChannel[i].channelId; return channelId; } + + int Mumlib::getUserIdBy(string name) { + vector listMumbleUser = impl->listMumbleUser; + int sessionId = -1; + for(int i = 0; i < listMumbleUser.size(); i++) + if(listMumbleUser[i].name == name) + sessionId = listMumbleUser[i].sessionId; + return sessionId; + } } From fe9a39ad5e3d8a9c61aea4c83b6217486429c0f4 Mon Sep 17 00:00:00 2001 From: Auzan Date: Wed, 23 May 2018 16:21:08 +0700 Subject: [PATCH 8/9] hack typo, adding frame to buffer --- CMakeLists.txt | 22 +++-- include/mumlib.hpp | 8 +- include/mumlib/Audio.hpp | 28 ++++++- src/Audio.cpp | 176 +++++++++++++++++++++++++++++++++++---- src/mumlib.cpp | 117 ++++++++++++++++---------- 5 files changed, 279 insertions(+), 72 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fbbab3..e4e177f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,15 +42,23 @@ set(MUMLIB_SRC src/Audio.cpp ) +set(SPEEX_LIBRARIES + speex + speexdsp + ) + PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS Mumble.proto) add_library(mumlib SHARED ${MUMLIB_SRC} ${MUMLIB_PUBLIC_HEADERS} ${MUMLIB_PRIVATE_HEADERS} ${PROTO_SRCS} ${PROTO_HDRS}) -target_link_libraries(mumlib ${PROTOBUF_LIBRARIES}) -target_link_libraries(mumlib ${Boost_LIBRARIES}) -target_link_libraries(mumlib ${OPENSSL_LIBRARIES}) -target_link_libraries(mumlib ${LOG4CPP_LIBRARIES}) -target_link_libraries(mumlib ${OPUS_LIBRARIES}) +target_link_libraries(mumlib + ${SPEEX_LIBRARIES} + ${PROTOBUF_LIBRARIES} + ${Boost_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${LOG4CPP_LIBRARIES} + ${OPUS_LIBRARIES}) +# add_executable(mumlib_example mumlib_example.cpp) +# target_link_libraries(mumlib_example mumlib) -add_executable(mumlib_example mumlib_example.cpp) -target_link_libraries(mumlib_example mumlib) +install(TARGETS mumlib LIBRARY DESTINATION lib) diff --git a/include/mumlib.hpp b/include/mumlib.hpp index cab8e31..08297d1 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -79,9 +79,9 @@ namespace mumlib { void joinChannel(std::string channelName); - void sendVoiceTarget(mumlib::VoiceTargetType type, int targetId, int id); + void sendVoiceTarget(int targetId, mumlib::VoiceTargetType type, int sessionId); - bool sendVoiceTarget(mumlib::VoiceTargetType type, int targetId, std::string name); + void sendVoiceTarget(int targetId, mumlib::VoiceTargetType type, std::string name, int &error); void sendUserState(mumlib::UserState state, bool val); @@ -93,5 +93,9 @@ namespace mumlib { int getChannelIdBy(std::string channelName); int getUserIdBy(std::string userName); + + bool isSessionIdValid(int sessionId); + + bool isChannelIdValid(int channelId); }; } diff --git a/include/mumlib/Audio.hpp b/include/mumlib/Audio.hpp index 7e1433a..19d89a3 100644 --- a/include/mumlib/Audio.hpp +++ b/include/mumlib/Audio.hpp @@ -4,6 +4,8 @@ #include +#include + #include namespace mumlib { @@ -26,14 +28,24 @@ namespace mumlib { class Audio : boost::noncopyable { public: - explicit Audio(int opusSampleRate=DEFAULT_OPUS_SAMPLE_RATE, - int opusEncoderBitrate=DEFAULT_OPUS_ENCODER_BITRATE, + explicit Audio(int sampleRate=DEFAULT_OPUS_SAMPLE_RATE, + int bitrate=DEFAULT_OPUS_ENCODER_BITRATE, int channels=DEFAULT_OPUS_NUM_CHANNELS); virtual ~Audio(); IncomingAudioPacket decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength); + void addFrameToBuffer(uint8_t *inputBuffer, int inputLength, int sequence); + + // todo: mix audio + void mixAudio(uint8_t *dest, uint8_t *src, int bufferOffset, int inputLength); + + void resizeBuffer(); + + std::pair decodeOpusPayload(int16_t *pcmBuffer, + int pcmBufferSize); + std::pair decodeOpusPayload(uint8_t *inputBuffer, int inputLength, int16_t *pcmBuffer, @@ -52,14 +64,24 @@ namespace mumlib { void resetEncoder(); + void resetJitterBuffer(); + private: log4cpp::Category &logger; OpusDecoder *opusDecoder; OpusEncoder *opusEncoder; + JitterBuffer *jbBuffer; int64_t outgoingSequenceNumber; - int sampleRate; + + unsigned int iSampleRate; + unsigned int iChannels; + unsigned int iFrameSize; + unsigned int iAudioBufferSize; + + float *fFadeIn; + float *fFadeOut; std::chrono::time_point lastEncodedAudioPacketTimestamp; }; diff --git a/src/Audio.cpp b/src/Audio.cpp index 60277df..393b674 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -4,30 +4,69 @@ static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5); -mumlib::Audio::Audio(int opusSampleRate, int opusEncoderBitrate, int channels) +mumlib::Audio::Audio(int sampleRate, int bitrate, int channels) : logger(log4cpp::Category::getInstance("mumlib.Audio")), opusDecoder(nullptr), opusEncoder(nullptr), - outgoingSequenceNumber(0) { + outgoingSequenceNumber(0), + iSampleRate(sampleRate), + iChannels(channels) { - int error; - this->sampleRate = opusSampleRate; + int error, ret; + iFrameSize = sampleRate / 100; + iAudioBufferSize = iFrameSize; + iAudioBufferSize *= 12; - opusDecoder = opus_decoder_create(opusSampleRate, channels, &error); + opusDecoder = opus_decoder_create(sampleRate, channels, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str()); } - opusEncoder = opus_encoder_create(opusSampleRate, channels, OPUS_APPLICATION_VOIP, &error); + opusEncoder = opus_encoder_create(sampleRate, channels, OPUS_APPLICATION_VOIP, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str()); } - opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0)); - - setOpusEncoderBitrate(opusEncoderBitrate); + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(bitrate)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize transmission bitrate to %d B/s: %s") + % bitrate % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize variable bitrate: %s") + % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_VBR_CONSTRAINT(0)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize variable bitrate constraint: %s") + % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize bandwidth narrow: %s") + % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize maximum bandwidth narrow: %s") + % opus_strerror(ret)).str()); + } resetEncoder(); + + jbBuffer = jitter_buffer_init(iFrameSize); + int margin = 10 * iFrameSize; + jitter_buffer_ctl(jbBuffer, JITTER_BUFFER_SET_MARGIN, &margin); + + fFadeIn = new float[iFrameSize]; + fFadeOut = new float[iFrameSize]; + + // Sine function to represent fade in/out. Period is FRAME_SIZE. + float mul = static_cast(M_PI / 2.0 * static_cast(iFrameSize)); + for(unsigned int i = 0; i < iFrameSize; i++) { + fFadeIn[i] = fFadeOut[iFrameSize - 1 - 1] = sinf(static_cast(i) * mul); + } } mumlib::Audio::~Audio() { @@ -38,6 +77,11 @@ mumlib::Audio::~Audio() { if (opusEncoder) { opus_encoder_destroy(opusEncoder); } + + jitter_buffer_destroy(jbBuffer); + + delete[] fFadeIn; + delete[] fFadeOut; } void mumlib::Audio::setOpusEncoderBitrate(int bitrate) { @@ -57,6 +101,105 @@ int mumlib::Audio::getOpusEncoderBitrate() { return bitrate; } +void mumlib::Audio::addFrameToBuffer(uint8_t *inputBuffer, int inputLength, int sequence) { + int dataPointer = 0; + VarInt varInt(inputBuffer); + int opusDataLength = varInt.getValue(); + dataPointer += varInt.getEncoded().size(); + bool lastPacket = (opusDataLength & 0x2000) != 0; + opusDataLength &= 0x1fff; + + auto *packet = reinterpret_cast(&inputBuffer[dataPointer]); + int frame = opus_packet_get_nb_frames(packet, opusDataLength); + int samples = frame * opus_packet_get_samples_per_frame(packet, iSampleRate); + int channel = opus_packet_get_nb_channels(packet); + + if(not sequence) { + resetJitterBuffer(); + } + + logger.info("Opus packet, frame: %d, samples: %d, channel: %d", frame, samples, channel); + + JitterBufferPacket jbPacket; + jbPacket.data = reinterpret_cast(&inputBuffer[dataPointer]); + jbPacket.len = opusDataLength; + jbPacket.span = samples; + jbPacket.timestamp = iFrameSize * sequence; + jbPacket.user_data = lastPacket; + + jitter_buffer_put(jbBuffer, &jbPacket); +} + +std::pair mumlib::Audio::decodeOpusPayload(int16_t *pcmBuffer, int pcmBufferSize) { + int avail = 0; + spx_uint32_t remaining = 0; + jitter_buffer_ctl(jbBuffer, JITTER_BUFFER_GET_AVAILABLE_COUNT, &avail); + jitter_buffer_remaining_span(jbBuffer, remaining); + int timestamp = jitter_buffer_get_pointer_timestamp(jbBuffer); + + logger.warn("jbBufer, avail: %d, remain: %d, timestamp: %d", avail, remaining, timestamp); + + char data[4096]; + JitterBufferPacket jbPacket; + jbPacket.data = data; + jbPacket.len = 4096; + + spx_int32_t startofs = 0; + int opusDataLength; + int outputSize; + spx_uint32_t lastPacket; + + if(jitter_buffer_get(jbBuffer, &jbPacket, iFrameSize, &startofs) == JITTER_BUFFER_OK) { + opusDataLength = jbPacket.len; + lastPacket = jbPacket.user_data; + } else { + jitter_buffer_update_delay(jbBuffer, &jbPacket, NULL); + } + + if(opusDataLength) { + outputSize = opus_decode(opusDecoder, + reinterpret_cast(jbPacket.data), + jbPacket.len, + pcmBuffer, + pcmBufferSize, 0); + } else { + outputSize = opus_decode(opusDecoder, + NULL, 0, pcmBuffer, pcmBufferSize, 0); + } + + if(outputSize < 0) { + outputSize = iFrameSize; + memset(pcmBuffer, 0, iFrameSize * sizeof(float)); + } + + if(lastPacket) { + for(unsigned int i = 0; i < iFrameSize; i++) + pcmBuffer[i] *= fFadeOut[i]; + } + + for (int i = outputSize / iFrameSize; i > 0; --i) { + jitter_buffer_tick(jbBuffer); + } + + logger.debug("%d B of Opus data decoded to %d PCM samples, last packet: %d.", + opusDataLength, outputSize, lastPacket); + + return std::make_pair(outputSize, lastPacket); +} + +void mumlib::Audio::mixAudio(uint8_t *dest, uint8_t *src, int bufferOffset, int inputLength) { + for(int i = 0; i < inputLength; i++) { + float mix = 0; + + // Clip to [-1,1] + if(mix > 1) + mix = 1; + else if(mix < -1) + mix = -1; + dest[i + bufferOffset] = mix; + } +} + std::pair mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer, int inputLength, int16_t *pcmBuffer, @@ -83,11 +226,11 @@ std::pair mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer, // Lost packets can be replaced with loss concealment by calling the decoder with a null pointer and zero length for the missing packet. // A single codec state may only be accessed from a single thread at a time and any required locking must be performed by the caller. // Separate streams must be decoded with separate decoder states and can be decoded in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK defined. - - int frame = opus_packet_get_nb_frames(&inputBuffer[dataPointer], opusDataLength); - int samples = frame * opus_packet_get_samples_per_frame(&inputBuffer[dataPointer], sampleRate); + auto *packet = reinterpret_cast(&inputBuffer[dataPointer]); + int frame = opus_packet_get_nb_frames(packet, opusDataLength); + int samples = frame * opus_packet_get_samples_per_frame(packet, iSampleRate); int outputSize = opus_decode(opusDecoder, - reinterpret_cast(&inputBuffer[dataPointer]), + packet, opusDataLength, pcmBuffer, pcmBufferSize, @@ -143,7 +286,7 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in memcpy(outputBuffer, &header[0], header.size()); memcpy(outputBuffer + header.size(), tmpOpusBuffer, (size_t) outputSize); - int incrementNumber = 100 * inputLength / sampleRate; + int incrementNumber = 100 * inputLength / iSampleRate; outgoingSequenceNumber += incrementNumber; @@ -162,6 +305,11 @@ void mumlib::Audio::resetEncoder() { outgoingSequenceNumber = 0; } +void mumlib::Audio::resetJitterBuffer() { + logger.debug("Last audio packet, resetting jitter buffer"); + jitter_buffer_reset(jbBuffer); +} + mumlib::IncomingAudioPacket mumlib::Audio::decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength) { mumlib::IncomingAudioPacket incomingAudioPacket{}; diff --git a/src/mumlib.cpp b/src/mumlib.cpp index c8101b5..3c85cec 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -33,6 +33,7 @@ namespace mumlib { int sessionId = 0; int channelId = 0; + int64_t seq = 0; std::vector listMumbleUser; std::vector listMumbleChannel; @@ -65,19 +66,27 @@ namespace mumlib { auto incomingAudioPacket = audio.decodeIncomingAudioPacket(buffer, length); if (type == AudioPacketType::OPUS) { - // todo: multiple users speaking simultaneously (Issue #3) - // something weird while decoding the opus payload int16_t pcmData[5000]; - auto status = audio.decodeOpusPayload(incomingAudioPacket.audioPayload, - incomingAudioPacket.audioPayloadLength, - pcmData, - 5000); + + audio.addFrameToBuffer(incomingAudioPacket.audioPayload, + incomingAudioPacket.audioPayloadLength, + seq); + + auto status = audio.decodeOpusPayload(pcmData, 5000); + + // auto status = audio.decodeOpusPayload(incomingAudioPacket.audioPayload, + // incomingAudioPacket.audioPayloadLength, + // pcmData, + // 5000); + + if(status.second) seq = 0; else seq++; callback.audio(incomingAudioPacket.target, - incomingAudioPacket.sessionId, - incomingAudioPacket.sequenceNumber, - pcmData, - status.first); + incomingAudioPacket.sessionId, + incomingAudioPacket.sequenceNumber, + pcmData, + status.first); + } else { logger.warn("Incoming audio packet doesn't contain Opus data, calling unsupportedAudio callback."); callback.unsupportedAudio(incomingAudioPacket.target, @@ -377,9 +386,12 @@ namespace mumlib { } void listUserRemovedBy(int sessionId) { - for(int i = 0; i < listMumbleUser.size(); i++) - if(listMumbleUser[i].sessionId == sessionId) + for(int i = 0; i < listMumbleUser.size(); i++) { + if(listMumbleUser[i].sessionId == sessionId) { listMumbleUser.erase(listMumbleUser.begin() + i); + return; + } + } } bool isListChannelContains(int channelId) { @@ -390,9 +402,12 @@ namespace mumlib { } void listChannelRemovedBy(int channelId) { - for(int i = 0; i < listMumbleChannel.size(); i++) - if(listMumbleChannel[i].channelId == channelId) + for(int i = 0; i < listMumbleChannel.size(); i++) { + if(listMumbleChannel[i].channelId == channelId) { listMumbleChannel.erase(listMumbleChannel.begin() + i); + return; + } + } } }; @@ -474,6 +489,8 @@ namespace mumlib { } void Mumlib::joinChannel(int channelId) { + if(!isChannelIdValid(channelId)) // when channel has not been registered / create + return; MumbleProto::UserState userState; userState.set_channel_id(channelId); impl->transport.sendControlMessage(MessageType::USERSTATE, userState); @@ -482,11 +499,10 @@ namespace mumlib { void Mumlib::joinChannel(string name) { int channelId = Mumlib::getChannelIdBy(name); - if(!channelId < 0) // when channel has not been registered / create - Mumlib::joinChannel(channelId); + Mumlib::joinChannel(channelId); } - void Mumlib::sendVoiceTarget(VoiceTargetType type, int targetId, int id) { + void Mumlib::sendVoiceTarget(int targetId, VoiceTargetType type, int id) { MumbleProto::VoiceTarget voiceTarget; MumbleProto::VoiceTarget_Target voiceTargetTarget; switch(type) { @@ -507,8 +523,8 @@ namespace mumlib { impl->transport.sendControlMessage(MessageType::VOICETARGET, voiceTarget); } - bool Mumlib::sendVoiceTarget(VoiceTargetType type, int targetId, string name) { - int id = -1; + void Mumlib::sendVoiceTarget(int targetId, VoiceTargetType type, string name, int &error) { + int id; switch(type) { case VoiceTargetType::CHANNEL: id = getChannelIdBy(name); @@ -519,10 +535,9 @@ namespace mumlib { default: break; } - if(id < 0) - return false; - sendVoiceTarget(type, targetId, id); - return true; + error = id < 0 ? 1: 0; + if(error) return; + sendVoiceTarget(targetId, type, id); } void Mumlib::sendUserState(mumlib::UserState field, bool val) { @@ -560,26 +575,22 @@ namespace mumlib { void Mumlib::sendUserState(mumlib::UserState field, std::string val) { MumbleProto::UserState userState; + + // if comment longer than 128 bytes, we need to set the SHA1 hash + // http://www.askyb.com/cpp/openssl-sha1-hashing-example-in-cpp/ + unsigned char digest[SHA_DIGEST_LENGTH]; + char mdString[SHA_DIGEST_LENGTH * 2 + 1]; + SHA1((unsigned char*) val.c_str(), val.size(), digest); + for(int i = 0; i < SHA_DIGEST_LENGTH; i++) + sprintf(&mdString[i*2], "%02x", (unsigned int) digest[i]); + switch (field) { case UserState::COMMENT: - - if(val.size() < 128) { + if(val.size() < 128) userState.set_comment(val); - } else { - // if comment longer than 128 bytes, we need to set the SHA1 hash - boost::uuids::detail::sha1 sha1; - uint hash[5]; - sha1.process_bytes(val.c_str(), val.size()); - sha1.get_digest(hash); - - std::stringstream valStream; - for(std::size_t i=0; i listMumbleChannel = impl->listMumbleChannel; - int channelId = -1; for(int i = 0; i < listMumbleChannel.size(); i++) if(listMumbleChannel[i].name == name) - channelId = listMumbleChannel[i].channelId; - return channelId; + return listMumbleChannel[i].channelId; + return -1; } int Mumlib::getUserIdBy(string name) { vector listMumbleUser = impl->listMumbleUser; - int sessionId = -1; for(int i = 0; i < listMumbleUser.size(); i++) if(listMumbleUser[i].name == name) - sessionId = listMumbleUser[i].sessionId; - return sessionId; + return listMumbleUser[i].sessionId; + return -1; + } + + bool Mumlib::isSessionIdValid(int sessionId) { + vector listMumbleUser = impl->listMumbleUser; + for(int i = 0; i < listMumbleUser.size(); i++) + if(listMumbleUser[i].sessionId == sessionId) + return true; + return false; + } + + bool Mumlib::isChannelIdValid(int channelId) { + vector listMumbleChannel = impl->listMumbleChannel; + for(int i = 0; i < listMumbleChannel.size(); i++) + if(listMumbleChannel[i].channelId == channelId) + return true; + return false; } } From 8ea7113d2197ce801797a7b67cdd9301a86a0961 Mon Sep 17 00:00:00 2001 From: Auzan Muhammad Date: Fri, 25 Jan 2019 15:06:18 +0700 Subject: [PATCH 9/9] update --- CMakeLists.txt | 8 ++ include/mumlib.hpp | 8 +- include/mumlib/Audio.hpp | 31 +++++++- src/Audio.cpp | 168 ++++++++++++++++++++++++++++++++++++--- src/Transport.cpp | 2 +- src/mumlib.cpp | 109 +++++++++++++++---------- 6 files changed, 267 insertions(+), 59 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fbbab3..aea43b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,9 +42,15 @@ set(MUMLIB_SRC src/Audio.cpp ) +set(MUMLIB_LIBRARIES + speex + speexdsp + ) + PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS Mumble.proto) add_library(mumlib SHARED ${MUMLIB_SRC} ${MUMLIB_PUBLIC_HEADERS} ${MUMLIB_PRIVATE_HEADERS} ${PROTO_SRCS} ${PROTO_HDRS}) +target_link_libraries(mumlib ${MUMLIB_LIBRARIES}) target_link_libraries(mumlib ${PROTOBUF_LIBRARIES}) target_link_libraries(mumlib ${Boost_LIBRARIES}) target_link_libraries(mumlib ${OPENSSL_LIBRARIES}) @@ -54,3 +60,5 @@ target_link_libraries(mumlib ${OPUS_LIBRARIES}) add_executable(mumlib_example mumlib_example.cpp) target_link_libraries(mumlib_example mumlib) + +install(TARGETS mumlib LIBRARY DESTINATION lib) diff --git a/include/mumlib.hpp b/include/mumlib.hpp index cab8e31..08297d1 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -79,9 +79,9 @@ namespace mumlib { void joinChannel(std::string channelName); - void sendVoiceTarget(mumlib::VoiceTargetType type, int targetId, int id); + void sendVoiceTarget(int targetId, mumlib::VoiceTargetType type, int sessionId); - bool sendVoiceTarget(mumlib::VoiceTargetType type, int targetId, std::string name); + void sendVoiceTarget(int targetId, mumlib::VoiceTargetType type, std::string name, int &error); void sendUserState(mumlib::UserState state, bool val); @@ -93,5 +93,9 @@ namespace mumlib { int getChannelIdBy(std::string channelName); int getUserIdBy(std::string userName); + + bool isSessionIdValid(int sessionId); + + bool isChannelIdValid(int channelId); }; } diff --git a/include/mumlib/Audio.hpp b/include/mumlib/Audio.hpp index 7e1433a..d75ac70 100644 --- a/include/mumlib/Audio.hpp +++ b/include/mumlib/Audio.hpp @@ -4,6 +4,8 @@ #include +#include + #include namespace mumlib { @@ -26,19 +28,30 @@ namespace mumlib { class Audio : boost::noncopyable { public: - explicit Audio(int opusSampleRate=DEFAULT_OPUS_SAMPLE_RATE, - int opusEncoderBitrate=DEFAULT_OPUS_ENCODER_BITRATE, + explicit Audio(int sampleRate=DEFAULT_OPUS_SAMPLE_RATE, + int bitrate=DEFAULT_OPUS_ENCODER_BITRATE, int channels=DEFAULT_OPUS_NUM_CHANNELS); virtual ~Audio(); IncomingAudioPacket decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength); + void addFrameToBuffer(uint8_t *inputBuffer, int inputLength, int sequence); + + void fetchAudio(uint8_t *inputBuffer, int bufferOffset, int inputLength); + + void mixAudio(uint8_t *inputBuffer, uint8_t *outputBuffer, int bufferOffset, int inputLength); + + void resizeBuffer(); + std::pair decodeOpusPayload(uint8_t *inputBuffer, int inputLength, int16_t *pcmBuffer, int pcmBufferSize); + std::pair decodeOpusPayload(int16_t *pcmBuffer, + int pcmBufferSize); + int encodeAudioPacket( int target, int16_t *inputPcmBuffer, @@ -52,14 +65,26 @@ namespace mumlib { void resetEncoder(); + void resetJitterBuffer(); + private: log4cpp::Category &logger; OpusDecoder *opusDecoder; OpusEncoder *opusEncoder; + JitterBuffer *jbBuffer; + + mutex m_jitter_mutex; int64_t outgoingSequenceNumber; - int sampleRate; + + unsigned int mSampleRate; + unsigned int mChannels; + unsigned int mFrameSize; + unsigned int mAudioBufferSize; + + float *mFadeIn; + float *mFadeOut; std::chrono::time_point lastEncodedAudioPacketTimestamp; }; diff --git a/src/Audio.cpp b/src/Audio.cpp index 60277df..80d8b72 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -4,30 +4,69 @@ static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5); -mumlib::Audio::Audio(int opusSampleRate, int opusEncoderBitrate, int channels) +mumlib::Audio::Audio(int sampleRate, int bitrate, int channels) : logger(log4cpp::Category::getInstance("mumlib.Audio")), opusDecoder(nullptr), opusEncoder(nullptr), outgoingSequenceNumber(0) { - int error; - this->sampleRate = opusSampleRate; + int error, ret; + mSampleRate = sampleRate; + mChannels = channels; + mFrameSize = sampleRate / 100; + mAudioBufferSize = mFrameSize; + mAudioBufferSize *= 12; - opusDecoder = opus_decoder_create(opusSampleRate, channels, &error); + opusDecoder = opus_decoder_create(sampleRate, channels, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str()); } - opusEncoder = opus_encoder_create(opusSampleRate, channels, OPUS_APPLICATION_VOIP, &error); + opusEncoder = opus_encoder_create(sampleRate, channels, OPUS_APPLICATION_VOIP, &error); if (error != OPUS_OK) { throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str()); } - opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0)); - - setOpusEncoderBitrate(opusEncoderBitrate); + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(bitrate)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize transmission bitrate to %d B/s: %s") + % bitrate % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize variable bitrate: %s") + % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_VBR_CONSTRAINT(0)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize variable bitrate constraint: %s") + % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize bandwidth narrow: %s") + % opus_strerror(ret)).str()); + } + ret = opus_encoder_ctl(opusEncoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); + if (ret != OPUS_OK) { + throw AudioException((boost::format("failed to initialize maximum bandwidth narrow: %s") + % opus_strerror(ret)).str()); + } resetEncoder(); + + jbBuffer = jitter_buffer_init(mFrameSize); + int margin = 10 * mFrameSize; + jitter_buffer_ctl(jbBuffer, JITTER_BUFFER_SET_MARGIN, &margin); + + mFadeIn = new float[mFrameSize]; + mFadeOut = new float[mFrameSize]; + + // Sine function to represent fade in/out. Period is FRAME_SIZE. + float mul = static_cast(M_PI / 2.0 * static_cast(mFrameSize)); + for(unsigned int i = 0; i < mFrameSize; i++) { + mFadeIn[i] = mFadeOut[mFrameSize - 1 - 1] = sinf(static_cast(i) * mul); + } } mumlib::Audio::~Audio() { @@ -38,6 +77,11 @@ mumlib::Audio::~Audio() { if (opusEncoder) { opus_encoder_destroy(opusEncoder); } + + jitter_buffer_destroy(jbBuffer); + + delete[] mFadeIn; + delete[] mFadeOut; } void mumlib::Audio::setOpusEncoderBitrate(int bitrate) { @@ -57,6 +101,99 @@ int mumlib::Audio::getOpusEncoderBitrate() { return bitrate; } +void mumlib::Audio::addFrameToBuffer(uint8_t *inputBuffer, int inputLength, int sequence) { + int dataPointer = 0; + VarInt varInt(inputBuffer); + int opusDataLength = varInt.getValue(); + dataPointer += varInt.getEncoded().size(); + bool lastPacket = (opusDataLength & 0x2000) != 0; + opusDataLength &= 0x1fff; + + auto *packet = reinterpret_cast(&inputBuffer[dataPointer]); + int frame = opus_packet_get_nb_frames(packet, opusDataLength); + int samples = frame * opus_packet_get_samples_per_frame(packet, mSampleRate); + + if(not sequence) { + resetJitterBuffer(); + } + + JitterBufferPacket jbPacket; + jbPacket.data = reinterpret_cast(&inputBuffer[dataPointer]); + jbPacket.len = opusDataLength; + jbPacket.span = samples; + jbPacket.timestamp = mFrameSize * sequence; + jbPacket.user_data = lastPacket; + + jitter_buffer_put(jbBuffer, &jbPacket); +} + +std::pair mumlib::Audio::decodeOpusPayload(int16_t *pcmBuffer, int pcmBufferSize) { + int avail = 0; + int ts = jitter_buffer_get_pointer_timestamp(jbBuffer); + jitter_buffer_ctl(jbBuffer, JITTER_BUFFER_GET_AVAILABLE_COUNT, &avail); + + char data[4096]; + JitterBufferPacket jbPacket; + jbPacket.data = data; + jbPacket.len = 4096; + + spx_int32_t startofs = 0; + int opusDataLength; + int outputSize; + spx_uint32_t lastPacket; + + if(jitter_buffer_get(jbBuffer, &jbPacket, mFrameSize, &startofs) == JITTER_BUFFER_OK) { + opusDataLength = jbPacket.len; + lastPacket = jbPacket.user_data; + + } else { + jitter_buffer_update_delay(jbBuffer, &jbPacket, NULL); + } + + if(opusDataLength) { + outputSize = opus_decode(opusDecoder, + reinterpret_cast(jbPacket.data), + jbPacket.len, + pcmBuffer, + pcmBufferSize, 0); + } else { + outputSize = opus_decode(opusDecoder, + NULL, 0, pcmBuffer, pcmBufferSize, 0); + } + + if(outputSize < 0) { + outputSize = mFrameSize; + memset(pcmBuffer, 0, mFrameSize * sizeof(float)); + } + + if(lastPacket) { + for(unsigned int i = 0; i < mFrameSize; i++) + pcmBuffer[i] *= mFadeOut[i]; + } + + for (int i = outputSize / mFrameSize; i > 0; --i) { + jitter_buffer_tick(jbBuffer); + } + + logger.debug("%d B of Opus data decoded to %d PCM samples, last packet: %d.", + opusDataLength, outputSize, lastPacket); + + return std::make_pair(outputSize, lastPacket); +} + +void mumlib::Audio::mixAudio(uint8_t *inputBuffer, uint8_t *outputBuffer, int bufferOffset, int inputLength) { + for(int i = 0; i < inputLength; i++) { + float mix = 0; + + // Clip to [-1,1] + if(mix > 1) + mix = 1; + else if(mix < -1) + mix = -1; + outputBuffer[i + bufferOffset] = mix; + } +} + std::pair mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer, int inputLength, int16_t *pcmBuffer, @@ -83,11 +220,11 @@ std::pair mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer, // Lost packets can be replaced with loss concealment by calling the decoder with a null pointer and zero length for the missing packet. // A single codec state may only be accessed from a single thread at a time and any required locking must be performed by the caller. // Separate streams must be decoded with separate decoder states and can be decoded in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK defined. - - int frame = opus_packet_get_nb_frames(&inputBuffer[dataPointer], opusDataLength); - int samples = frame * opus_packet_get_samples_per_frame(&inputBuffer[dataPointer], sampleRate); + auto *packet = reinterpret_cast(&inputBuffer[dataPointer]); + int frame = opus_packet_get_nb_frames(packet, opusDataLength); + int samples = frame * opus_packet_get_samples_per_frame(packet, mSampleRate); int outputSize = opus_decode(opusDecoder, - reinterpret_cast(&inputBuffer[dataPointer]), + packet, opusDataLength, pcmBuffer, pcmBufferSize, @@ -143,7 +280,7 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in memcpy(outputBuffer, &header[0], header.size()); memcpy(outputBuffer + header.size(), tmpOpusBuffer, (size_t) outputSize); - int incrementNumber = 100 * inputLength / sampleRate; + int incrementNumber = 100 * inputLength / mSampleRate; outgoingSequenceNumber += incrementNumber; @@ -162,6 +299,11 @@ void mumlib::Audio::resetEncoder() { outgoingSequenceNumber = 0; } +void mumlib::Audio::resetJitterBuffer() { + logger.debug("Last audio packet, resetting jitter buffer"); + jitter_buffer_reset(jbBuffer); +} + mumlib::IncomingAudioPacket mumlib::Audio::decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength) { mumlib::IncomingAudioPacket incomingAudioPacket{}; diff --git a/src/Transport.cpp b/src/Transport.cpp index 01b712a..83a383a 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -81,7 +81,7 @@ void mumlib::Transport::connect( doReceiveUdp(); } - + ip::tcp::resolver resolverTcp(ioService); ip::tcp::resolver::query queryTcp(host, to_string(port)); diff --git a/src/mumlib.cpp b/src/mumlib.cpp index c8101b5..eb33dad 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -33,6 +33,8 @@ namespace mumlib { int sessionId = 0; int channelId = 0; + int64_t seq = 0; + mutex jitter_mutex; std::vector listMumbleUser; std::vector listMumbleChannel; @@ -68,10 +70,21 @@ namespace mumlib { // todo: multiple users speaking simultaneously (Issue #3) // something weird while decoding the opus payload int16_t pcmData[5000]; - auto status = audio.decodeOpusPayload(incomingAudioPacket.audioPayload, - incomingAudioPacket.audioPayloadLength, - pcmData, - 5000); + + audio.addFrameToBuffer(incomingAudioPacket.audioPayload, + incomingAudioPacket.audioPayloadLength, + seq); + + auto status = audio.decodeOpusPayload(pcmData, 5000); + + // auto status = audio.decodeOpusPayload(incomingAudioPacket.audioPayload, + // incomingAudioPacket.audioPayloadLength, + // pcmData, + // 5000); + + if(status.second) seq = 0; else seq++; + + // logger.warn("Decode audio: %d , seq %d", incomingAudioPacket.sessionId, seq); callback.audio(incomingAudioPacket.target, incomingAudioPacket.sessionId, @@ -377,9 +390,12 @@ namespace mumlib { } void listUserRemovedBy(int sessionId) { - for(int i = 0; i < listMumbleUser.size(); i++) - if(listMumbleUser[i].sessionId == sessionId) + for(int i = 0; i < listMumbleUser.size(); i++) { + if(listMumbleUser[i].sessionId == sessionId) { listMumbleUser.erase(listMumbleUser.begin() + i); + return; + } + } } bool isListChannelContains(int channelId) { @@ -390,9 +406,12 @@ namespace mumlib { } void listChannelRemovedBy(int channelId) { - for(int i = 0; i < listMumbleChannel.size(); i++) - if(listMumbleChannel[i].channelId == channelId) + for(int i = 0; i < listMumbleChannel.size(); i++) { + if(listMumbleChannel[i].channelId == channelId) { listMumbleChannel.erase(listMumbleChannel.begin() + i); + return; + } + } } }; @@ -474,6 +493,8 @@ namespace mumlib { } void Mumlib::joinChannel(int channelId) { + if(!isChannelIdValid(channelId)) // when channel has not been registered / create + return; MumbleProto::UserState userState; userState.set_channel_id(channelId); impl->transport.sendControlMessage(MessageType::USERSTATE, userState); @@ -482,11 +503,10 @@ namespace mumlib { void Mumlib::joinChannel(string name) { int channelId = Mumlib::getChannelIdBy(name); - if(!channelId < 0) // when channel has not been registered / create - Mumlib::joinChannel(channelId); + Mumlib::joinChannel(channelId); } - void Mumlib::sendVoiceTarget(VoiceTargetType type, int targetId, int id) { + void Mumlib::sendVoiceTarget(int targetId, VoiceTargetType type, int id) { MumbleProto::VoiceTarget voiceTarget; MumbleProto::VoiceTarget_Target voiceTargetTarget; switch(type) { @@ -507,8 +527,8 @@ namespace mumlib { impl->transport.sendControlMessage(MessageType::VOICETARGET, voiceTarget); } - bool Mumlib::sendVoiceTarget(VoiceTargetType type, int targetId, string name) { - int id = -1; + void Mumlib::sendVoiceTarget(int targetId, VoiceTargetType type, string name, int &error) { + int id; switch(type) { case VoiceTargetType::CHANNEL: id = getChannelIdBy(name); @@ -519,10 +539,9 @@ namespace mumlib { default: break; } - if(id < 0) - return false; - sendVoiceTarget(type, targetId, id); - return true; + error = id < 0 ? 1: 0; + if(error) return; + sendVoiceTarget(targetId, type, id); } void Mumlib::sendUserState(mumlib::UserState field, bool val) { @@ -560,26 +579,22 @@ namespace mumlib { void Mumlib::sendUserState(mumlib::UserState field, std::string val) { MumbleProto::UserState userState; + + // if comment longer than 128 bytes, we need to set the SHA1 hash + // http://www.askyb.com/cpp/openssl-sha1-hashing-example-in-cpp/ + unsigned char digest[SHA_DIGEST_LENGTH]; + char mdString[SHA_DIGEST_LENGTH * 2 + 1]; + SHA1((unsigned char*) val.c_str(), val.size(), digest); + for(int i = 0; i < SHA_DIGEST_LENGTH; i++) + sprintf(&mdString[i*2], "%02x", (unsigned int) digest[i]); + switch (field) { case UserState::COMMENT: - - if(val.size() < 128) { + if(val.size() < 128) userState.set_comment(val); - } else { - // if comment longer than 128 bytes, we need to set the SHA1 hash - boost::uuids::detail::sha1 sha1; - uint hash[5]; - sha1.process_bytes(val.c_str(), val.size()); - sha1.get_digest(hash); - - std::stringstream valStream; - for(std::size_t i=0; i listMumbleChannel = impl->listMumbleChannel; - int channelId = -1; for(int i = 0; i < listMumbleChannel.size(); i++) if(listMumbleChannel[i].name == name) - channelId = listMumbleChannel[i].channelId; - return channelId; + return listMumbleChannel[i].channelId; + return -1; } int Mumlib::getUserIdBy(string name) { vector listMumbleUser = impl->listMumbleUser; - int sessionId = -1; for(int i = 0; i < listMumbleUser.size(); i++) if(listMumbleUser[i].name == name) - sessionId = listMumbleUser[i].sessionId; - return sessionId; + return listMumbleUser[i].sessionId; + return -1; + } + + bool Mumlib::isSessionIdValid(int sessionId) { + vector listMumbleUser = impl->listMumbleUser; + for(int i = 0; i < listMumbleUser.size(); i++) + if(listMumbleUser[i].sessionId == sessionId) + return true; + return false; + } + + bool Mumlib::isChannelIdValid(int channelId) { + vector listMumbleChannel = impl->listMumbleChannel; + for(int i = 0; i < listMumbleChannel.size(); i++) + if(listMumbleChannel[i].channelId == channelId) + return true; + return false; } }