Merge remote-tracking branch 'auzanmuh/develop'
This commit is contained in:
commit
d045e12c54
@ -42,15 +42,23 @@ set(MUMLIB_SRC
|
|||||||
src/Audio.cpp
|
src/Audio.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(SPEEX_LIBRARIES
|
||||||
|
speex
|
||||||
|
speexdsp
|
||||||
|
)
|
||||||
|
|
||||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS Mumble.proto)
|
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})
|
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
|
||||||
target_link_libraries(mumlib ${Boost_LIBRARIES})
|
${SPEEX_LIBRARIES}
|
||||||
target_link_libraries(mumlib ${OPENSSL_LIBRARIES})
|
${PROTOBUF_LIBRARIES}
|
||||||
target_link_libraries(mumlib ${LOG4CPP_LIBRARIES})
|
${Boost_LIBRARIES}
|
||||||
target_link_libraries(mumlib ${OPUS_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)
|
install(TARGETS mumlib LIBRARY DESTINATION lib)
|
||||||
target_link_libraries(mumlib_example mumlib)
|
|
||||||
|
35
Mumble.proto
35
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 <https://www.mumble.info/LICENSE>.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
package MumbleProto;
|
package MumbleProto;
|
||||||
|
|
||||||
option optimize_for = SPEED;
|
option optimize_for = SPEED;
|
||||||
@ -63,7 +70,8 @@ message Ping {
|
|||||||
// Sent by the server when it rejects the user connection.
|
// Sent by the server when it rejects the user connection.
|
||||||
message Reject {
|
message Reject {
|
||||||
enum RejectType {
|
enum RejectType {
|
||||||
// TODO ??
|
// The rejection reason is unknown (details should be available
|
||||||
|
// in Reject.reason).
|
||||||
None = 0;
|
None = 0;
|
||||||
// The client attempted to connect with an incompatible version.
|
// The client attempted to connect with an incompatible version.
|
||||||
WrongVersion = 1;
|
WrongVersion = 1;
|
||||||
@ -98,7 +106,7 @@ message ServerSync {
|
|||||||
optional uint32 max_bandwidth = 2;
|
optional uint32 max_bandwidth = 2;
|
||||||
// Server welcome text.
|
// Server welcome text.
|
||||||
optional string welcome_text = 3;
|
optional string welcome_text = 3;
|
||||||
// Current user permissions TODO: Confirm??
|
// Current user permissions in the root channel.
|
||||||
optional uint64 permissions = 4;
|
optional uint64 permissions = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +142,10 @@ message ChannelState {
|
|||||||
optional int32 position = 9 [default = 0];
|
optional int32 position = 9 [default = 0];
|
||||||
// SHA1 hash of the description if the description is 128 bytes or more.
|
// SHA1 hash of the description if the description is 128 bytes or more.
|
||||||
optional bytes description_hash = 10;
|
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
|
// 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;
|
optional bool self_deaf = 10;
|
||||||
// User image if it is less than 128 bytes.
|
// User image if it is less than 128 bytes.
|
||||||
optional bytes texture = 11;
|
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;
|
optional bytes plugin_context = 12;
|
||||||
// TODO ??
|
// The user's plugin-specific identity.
|
||||||
|
// This value is not transmitted to clients.
|
||||||
optional string plugin_identity = 13;
|
optional string plugin_identity = 13;
|
||||||
// User comment if it is less than 128 bytes.
|
// User comment if it is less than 128 bytes.
|
||||||
optional string comment = 14;
|
optional string comment = 14;
|
||||||
@ -209,7 +226,7 @@ message BanList {
|
|||||||
required uint32 mask = 2;
|
required uint32 mask = 2;
|
||||||
// User name for identification purposes (does not affect the ban).
|
// User name for identification purposes (does not affect the ban).
|
||||||
optional string name = 3;
|
optional string name = 3;
|
||||||
// TODO ??
|
// The certificate hash of the banned user.
|
||||||
optional string hash = 4;
|
optional string hash = 4;
|
||||||
// Reason for the ban (does not affect the ban).
|
// Reason for the ban (does not affect the ban).
|
||||||
optional string reason = 5;
|
optional string reason = 5;
|
||||||
@ -403,9 +420,9 @@ message VoiceTarget {
|
|||||||
message Target {
|
message Target {
|
||||||
// Users that are included as targets.
|
// Users that are included as targets.
|
||||||
repeated uint32 session = 1;
|
repeated uint32 session = 1;
|
||||||
// Channels that are included as targets.
|
// Channel that is included as a target.
|
||||||
optional uint32 channel_id = 2;
|
optional uint32 channel_id = 2;
|
||||||
// TODO ??
|
// ACL group that is included as a target.
|
||||||
optional string group = 3;
|
optional string group = 3;
|
||||||
// True if the voice should follow links from the specified channel.
|
// True if the voice should follow links from the specified channel.
|
||||||
optional bool links = 4 [default = false];
|
optional bool links = 4 [default = false];
|
||||||
@ -529,6 +546,8 @@ message ServerConfig {
|
|||||||
optional uint32 message_length = 4;
|
optional uint32 message_length = 4;
|
||||||
// Maximum image message length.
|
// Maximum image message length.
|
||||||
optional uint32 image_message_length = 5;
|
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
|
// Sent by the server to inform the clients of suggested client configuration
|
||||||
@ -541,4 +560,4 @@ message SuggestConfig {
|
|||||||
optional bool positional = 2;
|
optional bool positional = 2;
|
||||||
// True if the administrator suggests push to talk to be used on this server.
|
// True if the administrator suggests push to talk to be used on this server.
|
||||||
optional bool push_to_talk = 3;
|
optional bool push_to_talk = 3;
|
||||||
}
|
}
|
@ -11,6 +11,8 @@
|
|||||||
namespace mumlib {
|
namespace mumlib {
|
||||||
|
|
||||||
constexpr int DEFAULT_OPUS_ENCODER_BITRATE = 16000;
|
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 std;
|
||||||
using namespace boost::asio;
|
using namespace boost::asio;
|
||||||
@ -22,15 +24,28 @@ namespace mumlib {
|
|||||||
|
|
||||||
struct MumlibConfiguration {
|
struct MumlibConfiguration {
|
||||||
int opusEncoderBitrate = DEFAULT_OPUS_ENCODER_BITRATE;
|
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
|
// 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;
|
struct _Mumlib_Private;
|
||||||
|
|
||||||
|
|
||||||
class Mumlib : boost::noncopyable {
|
class Mumlib : boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
Mumlib(Callback &callback);
|
explicit Mumlib(Callback &callback);
|
||||||
|
|
||||||
Mumlib(Callback &callback, io_service &ioService);
|
Mumlib(Callback &callback, io_service &ioService);
|
||||||
|
|
||||||
@ -48,13 +63,39 @@ namespace mumlib {
|
|||||||
|
|
||||||
ConnectionState getConnectionState();
|
ConnectionState getConnectionState();
|
||||||
|
|
||||||
|
int getChannelId();
|
||||||
|
|
||||||
|
vector<MumbleUser> getListAllUser();
|
||||||
|
|
||||||
|
vector<MumbleChannel> getListAllChannel();
|
||||||
|
|
||||||
void sendAudioData(int16_t *pcmData, int pcmLength);
|
void sendAudioData(int16_t *pcmData, int pcmLength);
|
||||||
|
|
||||||
|
void sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength);
|
||||||
|
|
||||||
void sendTextMessage(std::string message);
|
void sendTextMessage(std::string message);
|
||||||
|
|
||||||
void joinChannel(int channelId);
|
void joinChannel(int channelId);
|
||||||
|
|
||||||
|
void joinChannel(std::string channelName);
|
||||||
|
|
||||||
|
void sendVoiceTarget(int targetId, mumlib::VoiceTargetType type, int sessionId);
|
||||||
|
|
||||||
|
void sendVoiceTarget(int targetId, mumlib::VoiceTargetType type, std::string name, int &error);
|
||||||
|
|
||||||
|
void sendUserState(mumlib::UserState state, bool val);
|
||||||
|
|
||||||
|
void sendUserState(mumlib::UserState state, std::string value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
_Mumlib_Private *impl;
|
_Mumlib_Private *impl;
|
||||||
|
|
||||||
|
int getChannelIdBy(std::string channelName);
|
||||||
|
|
||||||
|
int getUserIdBy(std::string userName);
|
||||||
|
|
||||||
|
bool isSessionIdValid(int sessionId);
|
||||||
|
|
||||||
|
bool isChannelIdValid(int channelId);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,19 @@
|
|||||||
|
|
||||||
#include "Transport.hpp"
|
#include "Transport.hpp"
|
||||||
|
|
||||||
#include <opus.h>
|
#include <opus/opus.h>
|
||||||
|
|
||||||
|
#include <speex/speex_jitter.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
namespace mumlib {
|
namespace mumlib {
|
||||||
|
|
||||||
constexpr int SAMPLE_RATE = 48000;
|
|
||||||
|
|
||||||
class MumlibException;
|
class MumlibException;
|
||||||
|
|
||||||
class AudioException : public MumlibException {
|
class AudioException : public MumlibException {
|
||||||
public:
|
public:
|
||||||
AudioException(string message) : MumlibException(message) { }
|
explicit AudioException(string message) : MumlibException(message) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IncomingAudioPacket {
|
struct IncomingAudioPacket {
|
||||||
@ -28,17 +28,30 @@ namespace mumlib {
|
|||||||
|
|
||||||
class Audio : boost::noncopyable {
|
class Audio : boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
Audio(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();
|
virtual ~Audio();
|
||||||
|
|
||||||
IncomingAudioPacket decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength);
|
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<int, bool> decodeOpusPayload(int16_t *pcmBuffer,
|
||||||
|
int pcmBufferSize);
|
||||||
|
|
||||||
std::pair<int, bool> decodeOpusPayload(uint8_t *inputBuffer,
|
std::pair<int, bool> decodeOpusPayload(uint8_t *inputBuffer,
|
||||||
int inputLength,
|
int inputLength,
|
||||||
int16_t *pcmBuffer,
|
int16_t *pcmBuffer,
|
||||||
int pcmBufferSize);
|
int pcmBufferSize);
|
||||||
|
|
||||||
|
|
||||||
int encodeAudioPacket(
|
int encodeAudioPacket(
|
||||||
int target,
|
int target,
|
||||||
int16_t *inputPcmBuffer,
|
int16_t *inputPcmBuffer,
|
||||||
@ -52,14 +65,25 @@ namespace mumlib {
|
|||||||
|
|
||||||
void resetEncoder();
|
void resetEncoder();
|
||||||
|
|
||||||
|
void resetJitterBuffer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
log4cpp::Category &logger;
|
log4cpp::Category &logger;
|
||||||
|
|
||||||
OpusDecoder *opusDecoder;
|
OpusDecoder *opusDecoder;
|
||||||
OpusEncoder *opusEncoder;
|
OpusEncoder *opusEncoder;
|
||||||
|
JitterBuffer *jbBuffer;
|
||||||
|
|
||||||
int64_t outgoingSequenceNumber;
|
int64_t outgoingSequenceNumber;
|
||||||
|
|
||||||
|
unsigned int iSampleRate;
|
||||||
|
unsigned int iChannels;
|
||||||
|
unsigned int iFrameSize;
|
||||||
|
unsigned int iAudioBufferSize;
|
||||||
|
|
||||||
|
float *fFadeIn;
|
||||||
|
float *fFadeOut;
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::system_clock> lastEncodedAudioPacketTimestamp;
|
std::chrono::time_point<std::chrono::system_clock> lastEncodedAudioPacketTimestamp;
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -153,7 +153,7 @@ namespace mumlib {
|
|||||||
|
|
||||||
~BasicCallback();
|
~BasicCallback();
|
||||||
|
|
||||||
virtual void version(
|
void version(
|
||||||
uint16_t major,
|
uint16_t major,
|
||||||
uint8_t minor,
|
uint8_t minor,
|
||||||
uint8_t patch,
|
uint8_t patch,
|
||||||
@ -161,29 +161,29 @@ namespace mumlib {
|
|||||||
string os,
|
string os,
|
||||||
string os_version) override;
|
string os_version) override;
|
||||||
|
|
||||||
virtual void audio(
|
void audio(
|
||||||
int target,
|
int target,
|
||||||
int sessionId,
|
int sessionId,
|
||||||
int sequenceNumber,
|
int sequenceNumber,
|
||||||
int16_t *pcm_data,
|
int16_t *pcm_data,
|
||||||
uint32_t pcm_data_size) override;
|
uint32_t pcm_data_size) override;
|
||||||
|
|
||||||
virtual void unsupportedAudio(
|
void unsupportedAudio(
|
||||||
int target,
|
int target,
|
||||||
int sessionId,
|
int sessionId,
|
||||||
int sequenceNumber,
|
int sequenceNumber,
|
||||||
uint8_t *encoded_audio_data,
|
uint8_t *encoded_audio_data,
|
||||||
uint32_t encoded_audio_data_size) override;
|
uint32_t encoded_audio_data_size) override;
|
||||||
|
|
||||||
virtual void serverSync(
|
void serverSync(
|
||||||
string welcome_text,
|
string welcome_text,
|
||||||
int32_t session,
|
int32_t session,
|
||||||
int32_t max_bandwidth,
|
int32_t max_bandwidth,
|
||||||
int64_t permissions) override;
|
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,
|
string name,
|
||||||
int32_t channel_id,
|
int32_t channel_id,
|
||||||
int32_t parent,
|
int32_t parent,
|
||||||
@ -194,13 +194,13 @@ namespace mumlib {
|
|||||||
bool temporary,
|
bool temporary,
|
||||||
int32_t position) override;
|
int32_t position) override;
|
||||||
|
|
||||||
virtual void userRemove(
|
void userRemove(
|
||||||
uint32_t session,
|
uint32_t session,
|
||||||
int32_t actor,
|
int32_t actor,
|
||||||
string reason,
|
string reason,
|
||||||
bool ban) override;
|
bool ban) override;
|
||||||
|
|
||||||
virtual void userState(
|
void userState(
|
||||||
int32_t session,
|
int32_t session,
|
||||||
int32_t actor,
|
int32_t actor,
|
||||||
string name,
|
string name,
|
||||||
@ -215,7 +215,7 @@ namespace mumlib {
|
|||||||
int32_t priority_speaker,
|
int32_t priority_speaker,
|
||||||
int32_t recording) override;
|
int32_t recording) override;
|
||||||
|
|
||||||
virtual void banList(
|
void banList(
|
||||||
const uint8_t *ip_data,
|
const uint8_t *ip_data,
|
||||||
uint32_t ip_data_size,
|
uint32_t ip_data_size,
|
||||||
uint32_t mask,
|
uint32_t mask,
|
||||||
@ -225,14 +225,14 @@ namespace mumlib {
|
|||||||
string start,
|
string start,
|
||||||
int32_t duration) override;
|
int32_t duration) override;
|
||||||
|
|
||||||
virtual void textMessage(
|
void textMessage(
|
||||||
uint32_t actor,
|
uint32_t actor,
|
||||||
std::vector<uint32_t> session,
|
std::vector<uint32_t> session,
|
||||||
std::vector<uint32_t> channel_id,
|
std::vector<uint32_t> channel_id,
|
||||||
std::vector<uint32_t> tree_id,
|
std::vector<uint32_t> tree_id,
|
||||||
string message) override;
|
string message) override;
|
||||||
|
|
||||||
virtual void permissionDenied(
|
void permissionDenied(
|
||||||
int32_t permission,
|
int32_t permission,
|
||||||
int32_t channel_id,
|
int32_t channel_id,
|
||||||
int32_t session,
|
int32_t session,
|
||||||
@ -240,48 +240,48 @@ namespace mumlib {
|
|||||||
int32_t deny_type,
|
int32_t deny_type,
|
||||||
string name) override;
|
string name) override;
|
||||||
|
|
||||||
virtual void queryUsers(
|
void queryUsers(
|
||||||
uint32_t n_ids,
|
uint32_t n_ids,
|
||||||
uint32_t *ids,
|
uint32_t *ids,
|
||||||
uint32_t n_names,
|
uint32_t n_names,
|
||||||
string *names) override;
|
string *names) override;
|
||||||
|
|
||||||
virtual void contextActionModify(
|
void contextActionModify(
|
||||||
string action,
|
string action,
|
||||||
string text,
|
string text,
|
||||||
uint32_t m_context,
|
uint32_t m_context,
|
||||||
uint32_t operation) override;
|
uint32_t operation) override;
|
||||||
|
|
||||||
virtual void contextAction(
|
void contextAction(
|
||||||
int32_t session,
|
int32_t session,
|
||||||
int32_t channel_id,
|
int32_t channel_id,
|
||||||
string action) override;
|
string action) override;
|
||||||
|
|
||||||
virtual void userList(
|
void userList(
|
||||||
uint32_t user_id,
|
uint32_t user_id,
|
||||||
string name,
|
string name,
|
||||||
string last_seen,
|
string last_seen,
|
||||||
int32_t last_channel) override;
|
int32_t last_channel) override;
|
||||||
|
|
||||||
virtual void permissionQuery(
|
void permissionQuery(
|
||||||
int32_t channel_id,
|
int32_t channel_id,
|
||||||
uint32_t permissions,
|
uint32_t permissions,
|
||||||
int32_t flush) override;
|
int32_t flush) override;
|
||||||
|
|
||||||
virtual void codecVersion(
|
void codecVersion(
|
||||||
int32_t alpha,
|
int32_t alpha,
|
||||||
int32_t beta,
|
int32_t beta,
|
||||||
uint32_t prefer_alpha,
|
uint32_t prefer_alpha,
|
||||||
int32_t opus) override;
|
int32_t opus) override;
|
||||||
|
|
||||||
virtual void serverConfig(
|
void serverConfig(
|
||||||
uint32_t max_bandwidth,
|
uint32_t max_bandwidth,
|
||||||
string welcome_text,
|
string welcome_text,
|
||||||
uint32_t allow_html,
|
uint32_t allow_html,
|
||||||
uint32_t message_length,
|
uint32_t message_length,
|
||||||
uint32_t image_message_length) override;
|
uint32_t image_message_length) override;
|
||||||
|
|
||||||
virtual void suggestConfig(
|
void suggestConfig(
|
||||||
uint32_t version,
|
uint32_t version,
|
||||||
uint32_t positional,
|
uint32_t positional,
|
||||||
uint32_t push_to_talk) override;
|
uint32_t push_to_talk) override;
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
namespace mumlib {
|
namespace mumlib {
|
||||||
|
|
||||||
class CryptState : boost::noncopyable {
|
class CryptState : boost::noncopyable {
|
||||||
public:
|
private:
|
||||||
unsigned char raw_key[AES_BLOCK_SIZE];
|
unsigned char raw_key[AES_BLOCK_SIZE];
|
||||||
unsigned char encrypt_iv[AES_BLOCK_SIZE];
|
unsigned char encrypt_iv[AES_BLOCK_SIZE];
|
||||||
unsigned char decrypt_iv[AES_BLOCK_SIZE];
|
unsigned char decrypt_iv[AES_BLOCK_SIZE];
|
||||||
@ -56,14 +56,19 @@ namespace mumlib {
|
|||||||
AES_KEY decrypt_key;
|
AES_KEY decrypt_key;
|
||||||
bool bInit;
|
bool bInit;
|
||||||
|
|
||||||
|
public:
|
||||||
CryptState();
|
CryptState();
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
|
void genKey();
|
||||||
|
|
||||||
void setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div);
|
void setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div);
|
||||||
|
|
||||||
void setDecryptIV(const unsigned char *iv);
|
void setDecryptIV(const unsigned char *iv);
|
||||||
|
|
||||||
|
const unsigned char* getEncryptIV() const;
|
||||||
|
|
||||||
void ocb_encrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len,
|
void ocb_encrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len,
|
||||||
const unsigned char *nonce,
|
const unsigned char *nonce,
|
||||||
unsigned char *tag);
|
unsigned char *tag);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <google/protobuf/message.h>
|
#include <google/protobuf/message.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace mumlib {
|
namespace mumlib {
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ namespace mumlib {
|
|||||||
|
|
||||||
class TransportException : public MumlibException {
|
class TransportException : public MumlibException {
|
||||||
public:
|
public:
|
||||||
TransportException(string message) : MumlibException(message) { }
|
TransportException(string message) : MumlibException(std::move(message)) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Transport : boost::noncopyable {
|
class Transport : boost::noncopyable {
|
||||||
|
@ -29,6 +29,6 @@ namespace mumlib {
|
|||||||
private:
|
private:
|
||||||
const int64_t value;
|
const int64_t value;
|
||||||
|
|
||||||
int64_t parseVariant(uint8_t *buffer);
|
int64_t parseVariant(const uint8_t *buffer);
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -45,4 +45,19 @@ namespace mumlib {
|
|||||||
OPUS
|
OPUS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class UserState {
|
||||||
|
MUTE,
|
||||||
|
DEAF,
|
||||||
|
SUPPRESS,
|
||||||
|
SELF_MUTE,
|
||||||
|
SELF_DEAF,
|
||||||
|
COMMENT,
|
||||||
|
PRIORITY_SPEAKER,
|
||||||
|
RECORDING
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class VoiceTargetType {
|
||||||
|
CHANNEL,
|
||||||
|
USER
|
||||||
|
};
|
||||||
}
|
}
|
187
src/Audio.cpp
187
src/Audio.cpp
@ -4,29 +4,69 @@
|
|||||||
|
|
||||||
static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5);
|
static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5);
|
||||||
|
|
||||||
mumlib::Audio::Audio(int opusEncoderBitrate)
|
mumlib::Audio::Audio(int sampleRate, int bitrate, int channels)
|
||||||
: logger(log4cpp::Category::getInstance("mumlib.Audio")),
|
: logger(log4cpp::Category::getInstance("mumlib.Audio")),
|
||||||
opusDecoder(nullptr),
|
opusDecoder(nullptr),
|
||||||
opusEncoder(nullptr),
|
opusEncoder(nullptr),
|
||||||
outgoingSequenceNumber(0) {
|
outgoingSequenceNumber(0),
|
||||||
|
iSampleRate(sampleRate),
|
||||||
|
iChannels(channels) {
|
||||||
|
|
||||||
int error;
|
int error, ret;
|
||||||
|
iFrameSize = sampleRate / 100;
|
||||||
|
iAudioBufferSize = iFrameSize;
|
||||||
|
iAudioBufferSize *= 12;
|
||||||
|
|
||||||
opusDecoder = opus_decoder_create(SAMPLE_RATE, 1, &error);
|
opusDecoder = opus_decoder_create(sampleRate, channels, &error);
|
||||||
if (error != OPUS_OK) {
|
if (error != OPUS_OK) {
|
||||||
throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str());
|
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(sampleRate, channels, OPUS_APPLICATION_VOIP, &error);
|
||||||
if (error != OPUS_OK) {
|
if (error != OPUS_OK) {
|
||||||
throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str());
|
throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0));
|
ret = opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(bitrate));
|
||||||
|
if (ret != OPUS_OK) {
|
||||||
setOpusEncoderBitrate(opusEncoderBitrate);
|
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();
|
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<float>(M_PI / 2.0 * static_cast<double>(iFrameSize));
|
||||||
|
for(unsigned int i = 0; i < iFrameSize; i++) {
|
||||||
|
fFadeIn[i] = fFadeOut[iFrameSize - 1 - 1] = sinf(static_cast<float>(i) * mul);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mumlib::Audio::~Audio() {
|
mumlib::Audio::~Audio() {
|
||||||
@ -37,6 +77,11 @@ mumlib::Audio::~Audio() {
|
|||||||
if (opusEncoder) {
|
if (opusEncoder) {
|
||||||
opus_encoder_destroy(opusEncoder);
|
opus_encoder_destroy(opusEncoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jitter_buffer_destroy(jbBuffer);
|
||||||
|
|
||||||
|
delete[] fFadeIn;
|
||||||
|
delete[] fFadeOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mumlib::Audio::setOpusEncoderBitrate(int bitrate) {
|
void mumlib::Audio::setOpusEncoderBitrate(int bitrate) {
|
||||||
@ -56,6 +101,105 @@ int mumlib::Audio::getOpusEncoderBitrate() {
|
|||||||
return bitrate;
|
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<const unsigned char *>(&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<char *>(&inputBuffer[dataPointer]);
|
||||||
|
jbPacket.len = opusDataLength;
|
||||||
|
jbPacket.span = samples;
|
||||||
|
jbPacket.timestamp = iFrameSize * sequence;
|
||||||
|
jbPacket.user_data = lastPacket;
|
||||||
|
|
||||||
|
jitter_buffer_put(jbBuffer, &jbPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, bool> 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<const unsigned char *>(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<int, bool> mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer,
|
std::pair<int, bool> mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer,
|
||||||
int inputLength,
|
int inputLength,
|
||||||
int16_t *pcmBuffer,
|
int16_t *pcmBuffer,
|
||||||
@ -75,8 +219,18 @@ std::pair<int, bool> mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer,
|
|||||||
% inputLength % dataPointer % opusDataLength).str());
|
% 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.
|
||||||
|
auto *packet = reinterpret_cast<const unsigned char *>(&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,
|
int outputSize = opus_decode(opusDecoder,
|
||||||
reinterpret_cast<const unsigned char *>(&inputBuffer[dataPointer]),
|
packet,
|
||||||
opusDataLength,
|
opusDataLength,
|
||||||
pcmBuffer,
|
pcmBuffer,
|
||||||
pcmBufferSize,
|
pcmBufferSize,
|
||||||
@ -108,7 +262,7 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in
|
|||||||
|
|
||||||
std::vector<uint8_t> header;
|
std::vector<uint8_t> header;
|
||||||
|
|
||||||
header.push_back(0x80 | target);
|
header.push_back(static_cast<unsigned char &&>(0x80 | target));
|
||||||
|
|
||||||
auto sequenceNumberEnc = VarInt(outgoingSequenceNumber).getEncoded();
|
auto sequenceNumberEnc = VarInt(outgoingSequenceNumber).getEncoded();
|
||||||
header.insert(header.end(), sequenceNumberEnc.begin(), sequenceNumberEnc.end());
|
header.insert(header.end(), sequenceNumberEnc.begin(), sequenceNumberEnc.end());
|
||||||
@ -130,15 +284,15 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in
|
|||||||
header.insert(header.end(), outputSizeEnc.begin(), outputSizeEnc.end());
|
header.insert(header.end(), outputSizeEnc.begin(), outputSizeEnc.end());
|
||||||
|
|
||||||
memcpy(outputBuffer, &header[0], header.size());
|
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 / iSampleRate;
|
||||||
|
|
||||||
outgoingSequenceNumber += incrementNumber;
|
outgoingSequenceNumber += incrementNumber;
|
||||||
|
|
||||||
lastEncodedAudioPacketTimestamp = std::chrono::system_clock::now();
|
lastEncodedAudioPacketTimestamp = std::chrono::system_clock::now();
|
||||||
|
|
||||||
return outputSize + header.size();
|
return static_cast<int>(outputSize + header.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void mumlib::Audio::resetEncoder() {
|
void mumlib::Audio::resetEncoder() {
|
||||||
@ -151,8 +305,13 @@ void mumlib::Audio::resetEncoder() {
|
|||||||
outgoingSequenceNumber = 0;
|
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 mumlib::Audio::decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength) {
|
||||||
mumlib::IncomingAudioPacket incomingAudioPacket;
|
mumlib::IncomingAudioPacket incomingAudioPacket{};
|
||||||
|
|
||||||
incomingAudioPacket.type = static_cast<AudioPacketType >((inputBuffer[0] & 0xE0) >> 5);
|
incomingAudioPacket.type = static_cast<AudioPacketType >((inputBuffer[0] & 0xE0) >> 5);
|
||||||
incomingAudioPacket.target = inputBuffer[0] & 0x1F;
|
incomingAudioPacket.target = inputBuffer[0] & 0x1F;
|
||||||
|
@ -35,7 +35,7 @@ void mumlib::BasicCallback::version(
|
|||||||
os_version.c_str());
|
os_version.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void mumlib::BasicCallback::audio(
|
void BasicCallback::audio(
|
||||||
int target,
|
int target,
|
||||||
int sessionId,
|
int sessionId,
|
||||||
int sequenceNumber,
|
int sequenceNumber,
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
#include "mumlib/CryptState.hpp"
|
#include "mumlib/CryptState.hpp"
|
||||||
|
|
||||||
|
#include <openssl/rand.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@ -57,6 +58,15 @@ bool mumlib::CryptState::isValid() const {
|
|||||||
return bInit;
|
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) {
|
void mumlib::CryptState::setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div) {
|
||||||
memcpy(raw_key, rkey, AES_BLOCK_SIZE);
|
memcpy(raw_key, rkey, AES_BLOCK_SIZE);
|
||||||
memcpy(encrypt_iv, eiv, 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);
|
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) {
|
void mumlib::CryptState::encrypt(const unsigned char *source, unsigned char *dst, unsigned int plain_length) {
|
||||||
unsigned char tag[AES_BLOCK_SIZE];
|
unsigned char tag[AES_BLOCK_SIZE];
|
||||||
|
|
||||||
|
@ -32,15 +32,15 @@ mumlib::Transport::Transport(
|
|||||||
bool noUdp) :
|
bool noUdp) :
|
||||||
logger(log4cpp::Category::getInstance("mumlib.Transport")),
|
logger(log4cpp::Category::getInstance("mumlib.Transport")),
|
||||||
ioService(ioService),
|
ioService(ioService),
|
||||||
processMessageFunction(processMessageFunc),
|
processMessageFunction(std::move(processMessageFunc)),
|
||||||
processEncodedAudioPacketFunction(processEncodedAudioPacketFunction),
|
processEncodedAudioPacketFunction(std::move(processEncodedAudioPacketFunction)),
|
||||||
noUdp(noUdp),
|
noUdp(noUdp),
|
||||||
state(ConnectionState::NOT_CONNECTED),
|
state(ConnectionState::NOT_CONNECTED),
|
||||||
udpSocket(ioService),
|
udpSocket(ioService),
|
||||||
sslContext(ssl::context::sslv23),
|
sslContext(ssl::context::sslv23),
|
||||||
sslSocket(ioService, sslContext),
|
sslSocket(ioService, sslContext),
|
||||||
pingTimer(ioService, PING_INTERVAL),
|
pingTimer(ioService, PING_INTERVAL),
|
||||||
asyncBufferPool(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH)) {
|
asyncBufferPool(static_cast<const unsigned long>(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH))) {
|
||||||
|
|
||||||
sslIncomingBuffer = new uint8_t[MAX_TCP_LENGTH];
|
sslIncomingBuffer = new uint8_t[MAX_TCP_LENGTH];
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ void mumlib::Transport::connect(
|
|||||||
|
|
||||||
doReceiveUdp();
|
doReceiveUdp();
|
||||||
}
|
}
|
||||||
|
|
||||||
ip::tcp::resolver resolverTcp(ioService);
|
ip::tcp::resolver resolverTcp(ioService);
|
||||||
ip::tcp::resolver::query queryTcp(host, to_string(port));
|
ip::tcp::resolver::query queryTcp(host, to_string(port));
|
||||||
|
|
||||||
@ -118,7 +118,6 @@ void mumlib::Transport::disconnect() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void mumlib::Transport::sendVersion() {
|
void mumlib::Transport::sendVersion() {
|
||||||
MumbleProto::Version version;
|
MumbleProto::Version version;
|
||||||
|
|
||||||
@ -185,10 +184,10 @@ void mumlib::Transport::doReceiveUdp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t plainBuffer[1024];
|
uint8_t plainBuffer[1024];
|
||||||
const int plainBufferLength = bytesTransferred - 4;
|
const int plainBufferLength = static_cast<const int>(bytesTransferred - 4);
|
||||||
|
|
||||||
bool success = cryptState.decrypt(
|
bool success = cryptState.decrypt(
|
||||||
udpIncomingBuffer, plainBuffer, bytesTransferred);
|
udpIncomingBuffer, plainBuffer, static_cast<unsigned int>(bytesTransferred));
|
||||||
|
|
||||||
if (not success) {
|
if (not success) {
|
||||||
throwTransportException("UDP packet decryption failed");
|
throwTransportException("UDP packet decryption failed");
|
||||||
@ -263,12 +262,12 @@ void mumlib::Transport::sendUdpAsync(uint8_t *buff, int length) {
|
|||||||
auto *encryptedMsgBuff = asyncBufferPool.malloc();
|
auto *encryptedMsgBuff = asyncBufferPool.malloc();
|
||||||
const int encryptedMsgLength = length + 4;
|
const int encryptedMsgLength = length + 4;
|
||||||
|
|
||||||
cryptState.encrypt(buff, reinterpret_cast<uint8_t *>(encryptedMsgBuff), length);
|
cryptState.encrypt(buff, reinterpret_cast<uint8_t *>(encryptedMsgBuff), static_cast<unsigned int>(length));
|
||||||
|
|
||||||
logger.debug("Sending %d B of data UDP asynchronously.", encryptedMsgLength);
|
logger.debug("Sending %d B of data UDP asynchronously.", encryptedMsgLength);
|
||||||
|
|
||||||
udpSocket.async_send_to(
|
udpSocket.async_send_to(
|
||||||
boost::asio::buffer(encryptedMsgBuff, length + 4),
|
boost::asio::buffer(encryptedMsgBuff, static_cast<size_t>(length + 4)),
|
||||||
udpReceiverEndpoint,
|
udpReceiverEndpoint,
|
||||||
[this, encryptedMsgBuff](const boost::system::error_code &ec, size_t bytesTransferred) {
|
[this, encryptedMsgBuff](const boost::system::error_code &ec, size_t bytesTransferred) {
|
||||||
asyncBufferPool.free(encryptedMsgBuff);
|
asyncBufferPool.free(encryptedMsgBuff);
|
||||||
@ -318,7 +317,7 @@ void mumlib::Transport::doReceiveSsl() {
|
|||||||
processMessageInternal(
|
processMessageInternal(
|
||||||
static_cast<MessageType>(messageType),
|
static_cast<MessageType>(messageType),
|
||||||
&sslIncomingBuffer[6],
|
&sslIncomingBuffer[6],
|
||||||
bytesTransferred - 6);
|
static_cast<int>(bytesTransferred - 6));
|
||||||
|
|
||||||
doReceiveSsl();
|
doReceiveSsl();
|
||||||
} else {
|
} else {
|
||||||
@ -438,10 +437,10 @@ void mumlib::Transport::sendUdpPing() {
|
|||||||
vector<uint8_t> message;
|
vector<uint8_t> message;
|
||||||
message.push_back(0x20);
|
message.push_back(0x20);
|
||||||
|
|
||||||
auto timestampVarint = VarInt(time(nullptr)).getEncoded();
|
auto timestampVarint = VarInt(static_cast<int64_t>(time(nullptr))).getEncoded();
|
||||||
message.insert(message.end(), timestampVarint.begin(), timestampVarint.end());
|
message.insert(message.end(), timestampVarint.begin(), timestampVarint.end());
|
||||||
|
|
||||||
sendUdpAsync(&message[0], message.size());
|
sendUdpAsync(&message[0], static_cast<int>(message.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void mumlib::Transport::sendSsl(uint8_t *buff, int length) {
|
void mumlib::Transport::sendSsl(uint8_t *buff, int length) {
|
||||||
@ -453,7 +452,7 @@ void mumlib::Transport::sendSsl(uint8_t *buff, int length) {
|
|||||||
logger.debug("Sending %d bytes of data.", length);
|
logger.debug("Sending %d bytes of data.", length);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
write(sslSocket, boost::asio::buffer(buff, length));
|
write(sslSocket, boost::asio::buffer(buff, static_cast<size_t>(length)));
|
||||||
} catch (boost::system::system_error &err) {
|
} catch (boost::system::system_error &err) {
|
||||||
throwTransportException(std::string("SSL send failed: ") + err.what());
|
throwTransportException(std::string("SSL send failed: ") + err.what());
|
||||||
}
|
}
|
||||||
@ -467,13 +466,13 @@ void mumlib::Transport::sendSslAsync(uint8_t *buff, int length) {
|
|||||||
|
|
||||||
auto *asyncBuff = asyncBufferPool.malloc();
|
auto *asyncBuff = asyncBufferPool.malloc();
|
||||||
|
|
||||||
memcpy(asyncBuff, buff, length);
|
memcpy(asyncBuff, buff, static_cast<size_t>(length));
|
||||||
|
|
||||||
logger.debug("Sending %d B of data asynchronously.", length);
|
logger.debug("Sending %d B of data asynchronously.", length);
|
||||||
|
|
||||||
async_write(
|
async_write(
|
||||||
sslSocket,
|
sslSocket,
|
||||||
boost::asio::buffer(asyncBuff, length),
|
boost::asio::buffer(asyncBuff, static_cast<size_t>(length)),
|
||||||
[this, asyncBuff](const boost::system::error_code &ec, size_t bytesTransferred) {
|
[this, asyncBuff](const boost::system::error_code &ec, size_t bytesTransferred) {
|
||||||
asyncBufferPool.free(asyncBuff);
|
asyncBufferPool.free(asyncBuff);
|
||||||
logger.debug("Sent %d B.", bytesTransferred);
|
logger.debug("Sent %d B.", bytesTransferred);
|
||||||
@ -499,7 +498,7 @@ void mumlib::Transport::sendControlMessagePrivate(MessageType type, google::prot
|
|||||||
const uint16_t type_network = htons(static_cast<uint16_t>(type));
|
const uint16_t type_network = htons(static_cast<uint16_t>(type));
|
||||||
|
|
||||||
const int size = message.ByteSize();
|
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;
|
const int length = sizeof(type_network) + sizeof(size_network) + size;
|
||||||
|
|
||||||
@ -517,7 +516,7 @@ void mumlib::Transport::sendControlMessagePrivate(MessageType type, google::prot
|
|||||||
void mumlib::Transport::throwTransportException(string message) {
|
void mumlib::Transport::throwTransportException(string message) {
|
||||||
state = ConnectionState::FAILED;
|
state = ConnectionState::FAILED;
|
||||||
|
|
||||||
throw TransportException(message);
|
throw TransportException(std::move(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) {
|
void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) {
|
||||||
@ -534,7 +533,7 @@ void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) {
|
|||||||
|
|
||||||
const uint16_t netUdptunnelType = htons(static_cast<uint16_t>(MessageType::UDPTUNNEL));
|
const uint16_t netUdptunnelType = htons(static_cast<uint16_t>(MessageType::UDPTUNNEL));
|
||||||
|
|
||||||
const uint32_t netLength = htonl(length);
|
const uint32_t netLength = htonl(static_cast<uint32_t>(length));
|
||||||
|
|
||||||
const int packet = sizeof(netUdptunnelType) + sizeof(netLength) + length;
|
const int packet = sizeof(netUdptunnelType) + sizeof(netLength) + length;
|
||||||
|
|
||||||
@ -542,14 +541,14 @@ void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) {
|
|||||||
|
|
||||||
memcpy(packetBuff, &netUdptunnelType, sizeof(netUdptunnelType));
|
memcpy(packetBuff, &netUdptunnelType, sizeof(netUdptunnelType));
|
||||||
memcpy(packetBuff + sizeof(netUdptunnelType), &netLength, sizeof(netLength));
|
memcpy(packetBuff + sizeof(netUdptunnelType), &netLength, sizeof(netLength));
|
||||||
memcpy(packetBuff + sizeof(netUdptunnelType) + sizeof(netLength), buffer, length);
|
memcpy(packetBuff + sizeof(netUdptunnelType) + sizeof(netLength), buffer, static_cast<size_t>(length));
|
||||||
|
|
||||||
sendSslAsync(packetBuff, length + sizeof(netUdptunnelType) + sizeof(netLength));
|
sendSslAsync(packetBuff, length + sizeof(netUdptunnelType) + sizeof(netLength));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mumlib::Transport::processAudioPacket(uint8_t *buff, int length) {
|
void mumlib::Transport::processAudioPacket(uint8_t *buff, int length) {
|
||||||
AudioPacketType type = static_cast<AudioPacketType >((buff[0] & 0xE0) >> 5);
|
auto type = static_cast<AudioPacketType >((buff[0] & 0xE0) >> 5);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AudioPacketType::CELT_Alpha:
|
case AudioPacketType::CELT_Alpha:
|
||||||
case AudioPacketType::Speex:
|
case AudioPacketType::Speex:
|
||||||
|
@ -12,7 +12,7 @@ mumlib::VarInt::VarInt(std::vector<uint8_t> encoded) : value(parseVariant(&encod
|
|||||||
* This code was taken from Mumble source code
|
* This code was taken from Mumble source code
|
||||||
* https://github.com/mumble-voip/mumble/blob/master/src/PacketDataStream.h
|
* 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];
|
int64_t v = buffer[0];
|
||||||
if ((v & 0x80) == 0x00) {
|
if ((v & 0x80) == 0x00) {
|
||||||
return (v & 0x7F);
|
return (v & 0x7F);
|
||||||
@ -36,6 +36,7 @@ int64_t mumlib::VarInt::parseVariant(uint8_t *buffer) {
|
|||||||
return (v & 0x1F) << 16 | buffer[1] << 8 | buffer[2];
|
return (v & 0x1F) << 16 | buffer[1] << 8 | buffer[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
throw VarIntException("invalid varint");
|
throw VarIntException("invalid varint");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ std::vector<uint8_t> mumlib::VarInt::getEncoded() const {
|
|||||||
if ((i & 0x8000000000000000LL) && (~i < 0x100000000LL)) {
|
if ((i & 0x8000000000000000LL) && (~i < 0x100000000LL)) {
|
||||||
i = ~i;
|
i = ~i;
|
||||||
if (i <= 0x3) {
|
if (i <= 0x3) {
|
||||||
encoded.push_back(0xFC | i);
|
encoded.push_back(static_cast<unsigned char &&>(0xFC | i));
|
||||||
return encoded;
|
return encoded;
|
||||||
} else {
|
} else {
|
||||||
encoded.push_back(0xF8);
|
encoded.push_back(0xF8);
|
||||||
@ -54,29 +55,29 @@ std::vector<uint8_t> mumlib::VarInt::getEncoded() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (i < 0x80) {
|
if (i < 0x80) {
|
||||||
encoded.push_back(i);
|
encoded.push_back(static_cast<unsigned char &&>(i));
|
||||||
} else if (i < 0x4000) {
|
} else if (i < 0x4000) {
|
||||||
encoded.push_back(0x80 | (i >> 8));
|
encoded.push_back(static_cast<unsigned char &&>(0x80 | (i >> 8)));
|
||||||
encoded.push_back(i & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>(i & 0xFF));
|
||||||
} else if (i < 0x200000) {
|
} else if (i < 0x200000) {
|
||||||
encoded.push_back(0xC0 | (i >> 16));
|
encoded.push_back(static_cast<unsigned char &&>(0xC0 | (i >> 16)));
|
||||||
encoded.push_back((i >> 8) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 8) & 0xFF));
|
||||||
encoded.push_back(i & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>(i & 0xFF));
|
||||||
} else if (i < 0x10000000) {
|
} else if (i < 0x10000000) {
|
||||||
encoded.push_back(0xE0 | (i >> 24));
|
encoded.push_back(static_cast<unsigned char &&>(0xE0 | (i >> 24)));
|
||||||
encoded.push_back((i >> 16) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 16) & 0xFF));
|
||||||
encoded.push_back((i >> 8) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 8) & 0xFF));
|
||||||
encoded.push_back(i & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>(i & 0xFF));
|
||||||
} else {
|
} else {
|
||||||
encoded.push_back(0xF4);
|
encoded.push_back(0xF4);
|
||||||
encoded.push_back((i >> 56) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 56) & 0xFF));
|
||||||
encoded.push_back((i >> 48) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 48) & 0xFF));
|
||||||
encoded.push_back((i >> 40) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 40) & 0xFF));
|
||||||
encoded.push_back((i >> 32) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 32) & 0xFF));
|
||||||
encoded.push_back((i >> 24) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 24) & 0xFF));
|
||||||
encoded.push_back((i >> 16) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 16) & 0xFF));
|
||||||
encoded.push_back((i >> 8) & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>((i >> 8) & 0xFF));
|
||||||
encoded.push_back(i & 0xFF);
|
encoded.push_back(static_cast<unsigned char &&>(i & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
return encoded;
|
return encoded;
|
||||||
|
251
src/mumlib.cpp
251
src/mumlib.cpp
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
#include <openssl/sha.h>
|
||||||
#include <log4cpp/Category.hh>
|
#include <log4cpp/Category.hh>
|
||||||
|
|
||||||
#include <Mumble.pb.h>
|
#include <Mumble.pb.h>
|
||||||
@ -32,6 +33,10 @@ namespace mumlib {
|
|||||||
|
|
||||||
int sessionId = 0;
|
int sessionId = 0;
|
||||||
int channelId = 0;
|
int channelId = 0;
|
||||||
|
int64_t seq = 0;
|
||||||
|
|
||||||
|
std::vector<MumbleUser> listMumbleUser;
|
||||||
|
std::vector<MumbleChannel> listMumbleChannel;
|
||||||
|
|
||||||
_Mumlib_Private(Callback &callback, MumlibConfiguration &configuration)
|
_Mumlib_Private(Callback &callback, MumlibConfiguration &configuration)
|
||||||
: _Mumlib_Private(callback, *(new io_service()), configuration) {
|
: _Mumlib_Private(callback, *(new io_service()), configuration) {
|
||||||
@ -43,7 +48,8 @@ namespace mumlib {
|
|||||||
ioService(ioService),
|
ioService(ioService),
|
||||||
externalIoService(true),
|
externalIoService(true),
|
||||||
transport(ioService, boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3),
|
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, configuration.opusChannels) {
|
||||||
|
|
||||||
audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate);
|
audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate);
|
||||||
}
|
}
|
||||||
@ -61,16 +67,26 @@ namespace mumlib {
|
|||||||
|
|
||||||
if (type == AudioPacketType::OPUS) {
|
if (type == AudioPacketType::OPUS) {
|
||||||
int16_t pcmData[5000];
|
int16_t pcmData[5000];
|
||||||
auto status = audio.decodeOpusPayload(incomingAudioPacket.audioPayload,
|
|
||||||
incomingAudioPacket.audioPayloadLength,
|
audio.addFrameToBuffer(incomingAudioPacket.audioPayload,
|
||||||
pcmData,
|
incomingAudioPacket.audioPayloadLength,
|
||||||
5000);
|
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,
|
callback.audio(incomingAudioPacket.target,
|
||||||
incomingAudioPacket.sessionId,
|
incomingAudioPacket.sessionId,
|
||||||
incomingAudioPacket.sequenceNumber,
|
incomingAudioPacket.sequenceNumber,
|
||||||
pcmData,
|
pcmData,
|
||||||
status.first);
|
status.first);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Incoming audio packet doesn't contain Opus data, calling unsupportedAudio callback.");
|
logger.warn("Incoming audio packet doesn't contain Opus data, calling unsupportedAudio callback.");
|
||||||
callback.unsupportedAudio(incomingAudioPacket.target,
|
callback.unsupportedAudio(incomingAudioPacket.target,
|
||||||
@ -122,6 +138,11 @@ namespace mumlib {
|
|||||||
case MessageType::CHANNELREMOVE: {
|
case MessageType::CHANNELREMOVE: {
|
||||||
MumbleProto::ChannelRemove channelRemove;
|
MumbleProto::ChannelRemove channelRemove;
|
||||||
channelRemove.ParseFromArray(buffer, length);
|
channelRemove.ParseFromArray(buffer, length);
|
||||||
|
|
||||||
|
if(isListChannelContains(channelRemove.channel_id())) {
|
||||||
|
listChannelRemovedBy(channelRemove.channel_id());
|
||||||
|
}
|
||||||
|
|
||||||
callback.channelRemove(channelRemove.channel_id());
|
callback.channelRemove(channelRemove.channel_id());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -152,7 +173,14 @@ namespace mumlib {
|
|||||||
links_remove.push_back(channelState.links_remove(i));
|
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(
|
callback.channelState(
|
||||||
channelState.name(),
|
channelState.name(),
|
||||||
@ -175,6 +203,10 @@ namespace mumlib {
|
|||||||
bool ban = user_remove.has_ban() ? user_remove.ban()
|
bool ban = user_remove.has_ban() ? user_remove.ban()
|
||||||
: false; //todo make sure it's correct to assume it's false
|
: false; //todo make sure it's correct to assume it's false
|
||||||
|
|
||||||
|
if(isListUserContains(user_remove.session())) {
|
||||||
|
listUserRemovedBy(user_remove.session());
|
||||||
|
}
|
||||||
|
|
||||||
callback.userRemove(
|
callback.userRemove(
|
||||||
user_remove.session(),
|
user_remove.session(),
|
||||||
actor,
|
actor,
|
||||||
@ -200,6 +232,18 @@ namespace mumlib {
|
|||||||
int32_t priority_speaker = userState.has_priority_speaker() ? userState.priority_speaker() : -1;
|
int32_t priority_speaker = userState.has_priority_speaker() ? userState.priority_speaker() : -1;
|
||||||
int32_t recording = userState.has_recording() ? userState.recording() : -1;
|
int32_t recording = userState.has_recording() ? userState.recording() : -1;
|
||||||
|
|
||||||
|
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,
|
callback.userState(session,
|
||||||
actor,
|
actor,
|
||||||
userState.name(),
|
userState.name(),
|
||||||
@ -334,7 +378,37 @@ namespace mumlib {
|
|||||||
return true;
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isListChannelContains(int channelId) {
|
||||||
|
for(int i = 0; i < listMumbleChannel.size(); i++)
|
||||||
|
if(listMumbleChannel[i].channelId == channelId)
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Mumlib::Mumlib(Callback &callback) {
|
Mumlib::Mumlib(Callback &callback) {
|
||||||
@ -363,13 +437,25 @@ namespace mumlib {
|
|||||||
return impl->transport.getConnectionState();
|
return impl->transport.getConnectionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Mumlib::getChannelId() {
|
||||||
|
return impl->channelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<mumlib::MumbleUser> Mumlib::getListAllUser() {
|
||||||
|
return impl->listMumbleUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<mumlib::MumbleChannel> Mumlib::getListAllChannel() {
|
||||||
|
return impl->listMumbleChannel;
|
||||||
|
}
|
||||||
|
|
||||||
void Mumlib::connect(string host, int port, string user, string password) {
|
void Mumlib::connect(string host, int port, string user, string password) {
|
||||||
impl->transport.connect(host, port, user, password);
|
impl->transport.connect(host, port, user, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mumlib::disconnect() {
|
void Mumlib::disconnect() {
|
||||||
if (not impl->externalIoService) {
|
if (not impl->externalIoService) {
|
||||||
impl->ioService.reset();
|
impl->ioService.stop();
|
||||||
}
|
}
|
||||||
if (impl->transport.getConnectionState() != ConnectionState::NOT_CONNECTED) {
|
if (impl->transport.getConnectionState() != ConnectionState::NOT_CONNECTED) {
|
||||||
impl->transport.disconnect();
|
impl->transport.disconnect();
|
||||||
@ -385,8 +471,12 @@ namespace mumlib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Mumlib::sendAudioData(int16_t *pcmData, int pcmLength) {
|
void Mumlib::sendAudioData(int16_t *pcmData, int pcmLength) {
|
||||||
|
Mumlib::sendAudioDataTarget(0, pcmData, pcmLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mumlib::sendAudioDataTarget(int targetId, int16_t *pcmData, int pcmLength) {
|
||||||
uint8_t encodedData[5000];
|
uint8_t encodedData[5000];
|
||||||
int length = impl->audio.encodeAudioPacket(0, pcmData, pcmLength, encodedData, 5000);
|
int length = impl->audio.encodeAudioPacket(targetId, pcmData, pcmLength, encodedData, 5000);
|
||||||
impl->transport.sendEncodedAudioPacket(encodedData, length);
|
impl->transport.sendEncodedAudioPacket(encodedData, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,9 +489,146 @@ namespace mumlib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Mumlib::joinChannel(int channelId) {
|
void Mumlib::joinChannel(int channelId) {
|
||||||
|
if(!isChannelIdValid(channelId)) // when channel has not been registered / create
|
||||||
|
return;
|
||||||
MumbleProto::UserState userState;
|
MumbleProto::UserState userState;
|
||||||
userState.set_channel_id(channelId);
|
userState.set_channel_id(channelId);
|
||||||
impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
|
impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
|
||||||
impl->channelId = channelId;
|
impl->channelId = channelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mumlib::joinChannel(string name) {
|
||||||
|
int channelId = Mumlib::getChannelIdBy(name);
|
||||||
|
Mumlib::joinChannel(channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mumlib::sendVoiceTarget(int targetId, VoiceTargetType type, int id) {
|
||||||
|
MumbleProto::VoiceTarget voiceTarget;
|
||||||
|
MumbleProto::VoiceTarget_Target voiceTargetTarget;
|
||||||
|
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, VoiceTargetType type, string name, int &error) {
|
||||||
|
int id;
|
||||||
|
switch(type) {
|
||||||
|
case VoiceTargetType::CHANNEL:
|
||||||
|
id = getChannelIdBy(name);
|
||||||
|
break;
|
||||||
|
case VoiceTargetType::USER:
|
||||||
|
id = getUserIdBy(name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
error = id < 0 ? 1: 0;
|
||||||
|
if(error) return;
|
||||||
|
sendVoiceTarget(targetId, type, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
userState.set_comment(val);
|
||||||
|
else
|
||||||
|
userState.set_comment_hash(mdString);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// in any other case, just ignore the command
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl->transport.sendControlMessage(MessageType::USERSTATE, userState);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Mumlib::getChannelIdBy(string name) {
|
||||||
|
vector<mumlib::MumbleChannel> listMumbleChannel = impl->listMumbleChannel;
|
||||||
|
for(int i = 0; i < listMumbleChannel.size(); i++)
|
||||||
|
if(listMumbleChannel[i].name == name)
|
||||||
|
return listMumbleChannel[i].channelId;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Mumlib::getUserIdBy(string name) {
|
||||||
|
vector<mumlib::MumbleUser> listMumbleUser = impl->listMumbleUser;
|
||||||
|
for(int i = 0; i < listMumbleUser.size(); i++)
|
||||||
|
if(listMumbleUser[i].name == name)
|
||||||
|
return listMumbleUser[i].sessionId;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mumlib::isSessionIdValid(int sessionId) {
|
||||||
|
vector<mumlib::MumbleUser> 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<mumlib::MumbleChannel> listMumbleChannel = impl->listMumbleChannel;
|
||||||
|
for(int i = 0; i < listMumbleChannel.size(); i++)
|
||||||
|
if(listMumbleChannel[i].channelId == channelId)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user