Skip to content

Commit

Permalink
Merge Mumble fixes (pr-2990)
Browse files Browse the repository at this point in the history
413c248 - fix(mumble): fix reinitializing clients when audio device is set
db1a9be - tweak(mumble): add some logging for when `developer 1` is set
94ac9db - fix(mumble): fix VS code freaking out that it cant cast an int -> uint
fc2ab16 - fix(mumble): fix server calls for `CryptSetup` invalidating MumbleCrypto
65b26b5 - tweak(mumble): let the client know whenever the server isn't responding to TCP packets
f182bb5 - fix(mumble): use `MumbleProto::Ping` packets for enabling/disabling udp
58109b3 - fix(mumble): limit buffer sizes to `1024` + other misc things
68da887 - tweak(mumble): don't recreate the UDP handle whenever the client stops receiving UDP packets
  • Loading branch information
prikolium-cfx committed Dec 26, 2024
2 parents 6370920 + 413c248 commit 8b97411
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 239 deletions.
57 changes: 48 additions & 9 deletions code/components/voip-mumble/include/MumbleClientImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <thread>

#include <uvw.hpp>
#include <botan/block_cipher.h>

namespace net
{
Expand Down Expand Up @@ -69,12 +70,51 @@ class MumbleTLSPolicy : public Botan::TLS::Policy
}
};


constexpr uint8_t AES_BLOCK_SIZE = 16;

class MumbleCrypto : public fwRefCountable
{
public:
virtual void Encrypt(const uint8_t* plain, uint8_t* cipher, size_t length) = 0;
uint32_t m_remoteGood;
uint32_t m_remoteLate;
uint32_t m_remoteLost;
uint32_t m_remoteResync;

uint32_t m_localGood;
uint32_t m_localLate;
uint32_t m_localLost;
uint32_t m_localResync;

void Encrypt(const uint8_t* plain, uint8_t* cipher, size_t length);
bool Decrypt(const uint8_t* cipher, uint8_t* plain, size_t length);
std::string GetClientNonce();

bool SetServerNonce(const std::string& serverNonce);
bool SetKey(const std::string& key, const std::string& clientNonce, const std::string& serverNonce);
bool IsInitialized() const;


MumbleCrypto()
{
memset(m_key.data(), 0, AES_BLOCK_SIZE);
memset(m_clientNonce.data(), 0, AES_BLOCK_SIZE);
memset(m_serverNonce.data(), 0, AES_BLOCK_SIZE);
}
private:
std::array<uint8_t, 16> m_key;
std::array<uint8_t, 16> m_clientNonce;
std::array<uint8_t, 16> m_serverNonce;

bool m_init;

virtual bool Decrypt(const uint8_t* cipher, uint8_t* plain, size_t length) = 0;
uint8_t m_decryptHistory[0x100];

std::unique_ptr<Botan::BlockCipher> m_cipher;

private:
void OCBEncrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len, const unsigned char *nonce, unsigned char *tag);
void OCBDecrypt(const unsigned char *encrypted, unsigned char *plain, unsigned int len, const unsigned char *nonce, unsigned char *tag);
};

class MumbleClient : public IMumbleClient, public Botan::TLS::Callbacks
Expand Down Expand Up @@ -195,14 +235,16 @@ class MumbleClient : public IMumbleClient, public Botan::TLS::Callbacks

int m_voiceTarget;

uint16_t m_inFlightTcpPings = 0;

bool m_hasUdp = false;

std::chrono::milliseconds m_lastUdp;

std::chrono::milliseconds m_nextPing;

TPositionHook m_positionHook;

fwRefContainer<MumbleCrypto> m_crypto;

std::string m_curManualChannel;

std::string m_lastManualChannel;
Expand All @@ -214,6 +256,8 @@ class MumbleClient : public IMumbleClient, public Botan::TLS::Callbacks
std::map<int, VoiceTargetConfig> m_pendingVoiceTargetUpdates;

public:
MumbleCrypto m_crypto;

static fwRefContainer<MumbleClient> GetCurrent();

inline int GetVoiceTarget() { return m_voiceTarget; }
Expand Down Expand Up @@ -244,11 +288,6 @@ class MumbleClient : public IMumbleClient, public Botan::TLS::Callbacks

void HandleUDP(const uint8_t* buf, size_t size);

inline void SetCrypto(const fwRefContainer<MumbleCrypto>& crypto)
{
m_crypto = crypto;
}

// Botan::TLS::Callbacks
public:
virtual inline void tls_emit_data(const uint8_t data[], size_t size) override
Expand Down
27 changes: 22 additions & 5 deletions code/components/voip-mumble/src/MumbleAudioInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ static InputIntentMode g_lastIntentMode = InputIntentMode::SPEECH;
void MumbleAudioInput::Initialize()
{
m_bitrateVar = std::make_shared<ConVar<int>>("voice_inBitrate", ConVar_None, m_curBitrate, &m_curBitrate);
// NOTE: If the maximum here is ever increased we need to properly split audio packets into smaller buffers so we don't send packets
// that are to large to the server, for FiveM mumble sending too large of a packet will result in the client being dropped, in the mumble-server
// implementation this will just cause the packet to be ignored.
m_bitrateVar->GetHelper()->SetConstraints(16000, 128000);

m_denoiseVar = std::make_shared<ConVar<bool>>("voice_enableNoiseSuppression", ConVar_Archive, m_denoise, &m_denoise);
Expand Down Expand Up @@ -269,7 +272,7 @@ void MumbleAudioInput::HandleData(const uint8_t* buffer, size_t numBytes)
m_apm->gain_control()->set_stream_analog_level(int(audioLevel * 255.0f));
}

int numVoice = 0;
bool hasVoice = false;

for (int off = 0; off < frameSize; off += 10)
{
Expand Down Expand Up @@ -304,6 +307,12 @@ void MumbleAudioInput::HandleData(const uint8_t* buffer, size_t numBytes)
}
#endif

// if we're using music speech intent we should always try to send voice
if (g_curInputIntentMode == InputIntentMode::MUSIC)
{
hasVoice = true;
}

// is this voice?
webrtc::AudioFrame frame;
frame.num_channels_ = 1;
Expand All @@ -318,13 +327,13 @@ void MumbleAudioInput::HandleData(const uint8_t* buffer, size_t numBytes)

if (m_apm->voice_detection()->stream_has_voice())
{
numVoice++;
hasVoice = true;
}

memcpy(&m_resampledBytes[frameStart], frame.data_, 480 * sizeof(int16_t));
}

if (m_mode == MumbleActivationMode::VoiceActivity && numVoice < 1)
if (m_mode == MumbleActivationMode::VoiceActivity && !hasVoice)
{
m_isTalking = false;
m_audioLevel = 0.0f;
Expand Down Expand Up @@ -398,6 +407,8 @@ void MumbleAudioInput::HandleData(const uint8_t* buffer, size_t numBytes)
SendQueuedOpusPackets();
}

constexpr uint16_t kMaxUdpPacket = 1024;

void MumbleAudioInput::EnqueueOpusPacket(std::string&& packet, int numFrames)
{
m_opusPackets.push({ std::move(packet), numFrames });
Expand All @@ -417,14 +428,20 @@ void MumbleAudioInput::SendQueuedOpusPackets()

const auto& [packet, frames] = packetChunk;

char outBuf[16384];
// Maximum size of Opus audio frames is 8191, but we *can't* actually send that much, FiveMs mumble will drop the player, while other implementations will just ignore the packet.
// This should be split into multiple packets for higher voice bitrates.
// https://mumble-protocol.readthedocs.io/en/latest/voice_data.html#packet-format
// https://mumble-protocol.readthedocs.io/en/latest/voice_data.html#opus-audio-frames
char outBuf[kMaxUdpPacket];
PacketDataStream buffer(outBuf, sizeof(outBuf));

buffer.append((4 << 5) | (m_client->GetVoiceTarget() & 31));

buffer << m_sequence;

bool bTerminate = false;
// This fixed stuttering a while back
// https://github.com/citizenfx/fivem/commit/6b341dc0d71a63a8992c18afe8e6048418978adc
constexpr bool bTerminate = false;

buffer << (packet.size() | (bTerminate ? (1 << 13) : 0));
buffer.append(packet.c_str(), packet.size());
Expand Down
21 changes: 6 additions & 15 deletions code/components/voip-mumble/src/MumbleAudioOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,9 @@ void MumbleAudioOutput::HandleClientConnect(const MumbleUser& user)
sendDescriptors[1].pOutputVoice = m_submixVoice;
}

const XAUDIO2_VOICE_SENDS sendList = { (m_submixVoice) ? 2 : 1, sendDescriptors };
uint32_t sendCount = (m_submixVoice) ? 2 : 1;

const XAUDIO2_VOICE_SENDS sendList = { sendCount , sendDescriptors };

IXAudio2SourceVoice* voice = nullptr;
HRESULT hr = xa2->CreateSourceVoice(&voice, &format, 0, 2.0f, state.get(), &sendList);
Expand Down Expand Up @@ -1497,28 +1499,17 @@ void MumbleAudioOutput::SetAudioDevice(const std::string& deviceId)

{
std::unique_lock<std::shared_mutex> _(m_clientsMutex);

for (auto& client : m_clients)
{
if (client.second)
{
m_ids.push_back(client.first);
}
}

// delete all clients
m_clients.clear();
}

// reinitialize audio device
InitializeAudioDevice();

// recreate clients
for (auto& client : m_ids)
m_client->GetState().ForAllUsers([this](const std::shared_ptr<MumbleUser>& user)
{
MumbleUser fakeUser(client);
HandleClientConnect(fakeUser);
}
HandleClientConnect(*user);
});
}

void MumbleAudioOutput::SetDistance(float distance)
Expand Down
Loading

0 comments on commit 8b97411

Please sign in to comment.