Supporting voice whisper again

This commit is contained in:
WolverinDEV 2020-11-28 11:09:25 +01:00
parent ccc3bad705
commit f0b094d7e4
18 changed files with 689 additions and 421 deletions

@ -1 +1 @@
Subproject commit be995e99c45ff7e5ba4adb36529a53558e06fa61 Subproject commit c08922955d079e74910e7a755d757b50fda3065f

View File

@ -154,6 +154,7 @@ set(SERVER_SOURCE_FILES
src/client/voice/ServerCommandExecutor.cpp src/client/voice/ServerCommandExecutor.cpp
src/client/voice/PingHandler.cpp src/client/voice/PingHandler.cpp
src/client/voice/CryptSetupHandler.cpp src/client/voice/CryptSetupHandler.cpp
src/client/shared/WhisperHandler.cpp
src/terminal/PipedTerminal.cpp src/terminal/PipedTerminal.cpp

View File

@ -267,6 +267,11 @@ namespace ts {
inline std::shared_ptr<ConnectedClient> ref() { return _this.lock(); } inline std::shared_ptr<ConnectedClient> ref() { return _this.lock(); }
std::shared_mutex& get_channel_lock() { return this->channel_lock; } std::shared_mutex& get_channel_lock() { return this->channel_lock; }
/* Attention: Ensure that channel_lock has been locked */
[[nodiscard]] inline std::vector<GroupId>& current_server_groups() { return this->cached_server_groups; }
[[nodiscard]] inline GroupId& current_channel_group() { return this->cached_channel_group; }
/* /*
* permission stuff * permission stuff
*/ */

View File

@ -14,6 +14,7 @@
#include "StringVariable.h" #include "StringVariable.h"
#include "misc/timer.h" #include "misc/timer.h"
#include "../manager/ActionLogger.h" #include "../manager/ActionLogger.h"
#include "./voice/VoiceClient.h"
using namespace std::chrono; using namespace std::chrono;
using namespace ts; using namespace ts;
@ -26,7 +27,7 @@ constexpr static auto kMaxWhisperClientNameLength{30};
constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */ constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */
constexpr static auto kWhisperMaxHeaderLength{2 + 2 + 1 + 2 + kWhisperClientUniqueIdLength + 1 + kMaxWhisperClientNameLength}; constexpr static auto kWhisperMaxHeaderLength{2 + 2 + 1 + 2 + kWhisperClientUniqueIdLength + 1 + kMaxWhisperClientNameLength};
SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient(a, b) { SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient(a, b), whisper_handler_{this} {
speak_begin = std::chrono::system_clock::now(); speak_begin = std::chrono::system_clock::now();
speak_last_packet = std::chrono::system_clock::now(); speak_last_packet = std::chrono::system_clock::now();
}; };
@ -68,29 +69,6 @@ bool SpeakingClient::should_handle_voice_packet(size_t) {
return true; return true;
} }
//2 + 2 + 8
#define OUT_WHISPER_PKT_OFFSET 5
//#define PKT_LOG_WHISPER
enum WhisperType {
SERVER_GROUP = 0,
CHANNEL_GROUP = 1,
CHANNEL_COMMANDER = 2,
ALL = 3,
ECHO = 0x10,
};
enum WhisperTarget {
CHANNEL_ALL = 0,
CHANNEL_CURRENT = 1,
CHANNEL_PARENT = 2,
CHANNEL_ALL_PARENT = 3,
CHANNEL_FAMILY = 4,
CHANNEL_COMPLETE_FAMILY = 5,
CHANNEL_SUBCHANNELS = 6
};
inline bool update_whisper_error(std::chrono::system_clock::time_point& last) { inline bool update_whisper_error(std::chrono::system_clock::time_point& last) {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
if(last + std::chrono::milliseconds{500} < now) { if(last + std::chrono::milliseconds{500} < now) {
@ -100,277 +78,6 @@ inline bool update_whisper_error(std::chrono::system_clock::time_point& last) {
return false; return false;
} }
//All clients => type := SERVER_GROUP and target_id := 0
//Server group => type := SERVER_GROUP and target_id := <server group id>
//Channel group => type := CHANNEL_GROUP and target_id := <channel group id>
//Channel commander => type := CHANNEL_COMMANDER and target_id := 0
void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& payload, bool new_packet, bool head) {
if(payload.length() < 5) {
this->disconnect("Invalid packet (Voice whisper)");
logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, payload.length());
return;
}
uint16_t payload_offset{0};
auto voice_packet_id = be2le16((char*) payload.data_ptr(), payload_offset, &payload_offset);
auto voice_codec = (uint8_t) payload[payload_offset++];
std::deque<std::shared_ptr<SpeakingClient>> target_clients;
if(new_packet) {
if(payload.length() < 7) {
this->disconnect("Invalid packet (Voice whisper | New)");
logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, payload.length());
return;
}
auto type = (WhisperType) payload[payload_offset++];
auto target = (WhisperTarget) payload[payload_offset++];
auto type_id = be2le64((char*) payload.data_ptr(), payload_offset, &payload_offset);
#ifdef PKT_LOG_WHISPER
logTrace(this->getServerId(), "{} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}.", CLIENT_STR_LOG_PREFIX, data_length, type, target, type_id);
#endif
if(type == WhisperType::ECHO) {
target_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->ref()));
} else {
for(const auto& client : this->server->getClients()) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(client);
if(!speakingClient || client == this) continue;
if(!speakingClient->currentChannel) continue;
if(type == WhisperType::ALL) {
target_clients.push_back(speakingClient);
} else if(type == WhisperType::SERVER_GROUP) {
if(type_id == 0)
target_clients.push_back(speakingClient);
else {
shared_lock client_lock(this->channel_lock);
for(const auto& id : client->cached_server_groups) {
if(id == type_id) {
target_clients.push_back(speakingClient);
break;
}
}
}
} else if(type == WhisperType::CHANNEL_GROUP) {
if(client->cached_channel_group == type_id)
target_clients.push_back(speakingClient);
} else if(type == WhisperType::CHANNEL_COMMANDER) {
if(client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as<bool>())
target_clients.push_back(speakingClient);
}
}
if(target == WhisperTarget::CHANNEL_CURRENT) {
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
return target->currentChannel != this->currentChannel;
}), target_clients.end());
} else if(target == WhisperTarget::CHANNEL_PARENT) {
auto current_parent = this->currentChannel->parent();
if(!current_parent) return;
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
return target->currentChannel != current_parent;
}), target_clients.end());
} else if(target == WhisperTarget::CHANNEL_ALL_PARENT) {
shared_ptr<BasicChannel> current_parent;
{
current_parent = this->currentChannel->parent();
if(!current_parent) return;
}
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
auto tmp_parent = current_parent;
while(tmp_parent && tmp_parent != target->currentChannel)
tmp_parent = tmp_parent->parent();
return target->currentChannel != tmp_parent;
}), target_clients.end());
} else if(target == WhisperTarget::CHANNEL_FAMILY) {
shared_ptr<BasicChannel> current = this->currentChannel;
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
auto tmp_current = target->currentChannel;
while(tmp_current && tmp_current != current)
tmp_current = tmp_current->parent();
return current != tmp_current;
}), target_clients.end());
} else if(target == WhisperTarget::CHANNEL_COMPLETE_FAMILY) {
shared_ptr<BasicChannel> current = this->currentChannel;
while(current && current->parent()) current = current->parent();
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
auto tmp_current = target->currentChannel;
while(tmp_current && tmp_current != current)
tmp_current = tmp_current->parent();
return current != tmp_current;
}), target_clients.end());
} else if(target == WhisperTarget::CHANNEL_SUBCHANNELS) {
shared_ptr<BasicChannel> current = this->currentChannel;
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient>& target) {
return target->currentChannel->parent() != current;
}), target_clients.end());
}
auto self_lock = this->_this.lock();
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
}), target_clients.end());
if(target_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
this->notifyError(result);
}
return;
}
if(target_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
}
return;
}
}
} else {
auto channelCount = (uint8_t) payload[payload_offset++];
auto clientCount = (uint8_t) payload[payload_offset++];
if(payload.length() < 5 + clientCount * 2 + channelCount * 8) {
logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {} Required: {}", CLIENT_STR_LOG_PREFIX, payload.length(), to_string(5 + channelCount * 2 + clientCount * 8));
return;
}
ChannelId channelIds[channelCount];
ClientId clientIds[clientCount];
for(uint8_t index = 0; index < channelCount; index++) {
channelIds[index] = be2le64((char*) payload.data_ptr(), payload_offset, &payload_offset);
}
for(uint8_t index = 0; index < clientCount; index++) {
clientIds[index] = be2le16((char*) payload.data_ptr(), payload_offset, &payload_offset);
}
#ifdef PKT_LOG_WHISPER
logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount);
#endif
for(const auto& client : this->server->getClients()) {
auto speaking_client = dynamic_pointer_cast<SpeakingClient>(client);
if(!speaking_client || client == this || !speaking_client->currentChannel)
continue;
auto clientChannelId = speaking_client->getChannelId();
auto clientId = speaking_client->getClientId();
for(uint8_t index = 0; index < channelCount; index++) {
if(channelIds[index] == clientChannelId) {
goto add_client;
}
}
for(uint8_t index = 0; index < clientCount; index++) {
if(clientIds[index] == clientId) {
goto add_client;
}
}
continue;
add_client:
if(!speaking_client->shouldReceiveVoiceWhisper(this->ref())) {
continue;
}
target_clients.push_back(speaking_client);
}
}
if(target_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
this->notifyError(result);
}
return;
}
if(target_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
}
return;
}
/* send the packet */
{
size_t voice_payload_length = payload.length() - payload_offset;
//Create the packet data
char whisper_packet_buffer[kWhisperMaxHeaderLength + voice_payload_length];
size_t whisper_packet_offset{0};
size_t whisper_packet_teamspeak_offset{0};
/* writing the teaspeak header */
if(head) {
auto uniqueId = this->getUid();
auto nickname = this->getDisplayName();
if(uniqueId.length() > kWhisperClientUniqueIdLength) {
logCritical(LOG_GENERAL, "Clients unique id is longer than the expected max length of {}. Unique length: {}", kWhisperClientUniqueIdLength, uniqueId.length());
return;
}
if(nickname.length() > kMaxWhisperClientNameLength) {
logCritical(LOG_GENERAL, "Clients name is longer than the expected max length of {}. Name length: {}", kMaxWhisperClientNameLength, nickname.length());
return;
}
memset(whisper_packet_buffer + whisper_packet_offset, 0, kWhisperClientUniqueIdLength);
memcpy(whisper_packet_buffer + whisper_packet_offset, uniqueId.data(), uniqueId.length());
whisper_packet_offset += kWhisperClientUniqueIdLength;
whisper_packet_buffer[whisper_packet_offset++] = nickname.length();
memcpy(whisper_packet_buffer + whisper_packet_offset, nickname.data(), nickname.length());
whisper_packet_offset += nickname.length();
}
/* writing the "normal" header and payload */
{
whisper_packet_teamspeak_offset = whisper_packet_offset;
*(uint16_t*) &whisper_packet_buffer[whisper_packet_offset] = htons(voice_packet_id);
whisper_packet_offset += 2;
*(uint16_t*) &whisper_packet_buffer[whisper_packet_offset] = htons(this->getClientId());
whisper_packet_offset += 2;
whisper_packet_buffer[whisper_packet_offset++] = voice_codec;
if(voice_payload_length > 0) {
memcpy(&whisper_packet_buffer[whisper_packet_offset], &payload[payload_offset], voice_payload_length);
whisper_packet_offset += voice_payload_length;
}
}
VoicePacketFlags flags{};
flags.head = head;
pipes::buffer_view teaspeak_packet{}, teamspeak_packet{};
teaspeak_packet = pipes::buffer_view{whisper_packet_buffer, whisper_packet_offset};
teamspeak_packet = pipes::buffer_view{whisper_packet_buffer + whisper_packet_teamspeak_offset, whisper_packet_offset - whisper_packet_teamspeak_offset};
auto self_ref = this->ref();
for(const auto& cl : target_clients) {
if(cl == self_ref || cl->shouldReceiveVoiceWhisper(self_ref)) {
cl->send_voice_whisper_packet(teamspeak_packet, teaspeak_packet, flags);
}
}
}
this->resetIdleTime();
this->updateSpeak(false, std::chrono::system_clock::now());
}
#define TEST_PARM(type) \ #define TEST_PARM(type) \
do {\ do {\
if(!cmd[0][key].castable<type>())\ if(!cmd[0][key].castable<type>())\
@ -704,26 +411,28 @@ void SpeakingClient::processJoin() {
TIMING_STEP(timings, "setup "); TIMING_STEP(timings, "setup ");
ref_server->registerClient(_this.lock()); ref_server->registerClient(_this.lock());
{
if(this->rtc_client_id) { if(this->rtc_client_id) {
/* in case of client reconnect */ /* in case of client reconnect */
this->server->rtc_server().destroy_client(this->rtc_client_id); this->server->rtc_server().destroy_client(this->rtc_client_id);
} }
if(this->getType() == ClientType::CLIENT_TEAMSPEAK) {
this->rtc_client_id = this->server->rtc_server().create_native_client(dynamic_pointer_cast<SpeakingClient>(this->ref())); std::string error{};
} else if(this->getType() == ClientType::CLIENT_TEASPEAK) { this->rtc_client_id = this->server->rtc_server().create_client(dynamic_pointer_cast<SpeakingClient>(this->ref()));
/* TODO: Will be a RTP client later on, just without audio */
this->rtc_client_id = this->server->rtc_server().create_native_client(dynamic_pointer_cast<SpeakingClient>(this->ref())); if(auto voice_client{dynamic_cast<VoiceClient*>(this)}; voice_client) {
} else if(this->getType() == ClientType::CLIENT_WEB) { if(!this->server->rtc_server().initialize_native_connection(error, this->rtc_client_id)) {
std::string error; logCritical(this->getServerId(), "{} Native connection setup failed: {}", CLIENT_STR_LOG_PREFIX, error);
auto result = this->server->rtc_server().create_rtp_client(dynamic_pointer_cast<SpeakingClient>(this->ref()), error); }
if(result > 0) { }
this->rtc_client_id = result; if(this->getType() == ClientType::CLIENT_WEB || this->getType() == ClientType::CLIENT_TEASPEAK) {
if(!this->server->rtc_server().initialize_rtc_connection(error, this->rtc_client_id)) {
logCritical(this->getServerId(), "{} RTC connection setup failed: {}", CLIENT_STR_LOG_PREFIX, error);
} else { } else {
this->rtc_client_id = 0;
logCritical(this->getServerId(), "{} Failed to configure RTC session: {}", CLIENT_STR_LOG_PREFIX, error);
}
}
this->rtc_session_pending_describe = true; this->rtc_session_pending_describe = true;
}
}
}
TIMING_STEP(timings, "server reg "); TIMING_STEP(timings, "server reg ");
ref_server->getGroupManager()->cleanupAssignments(this->getClientDatabaseId()); ref_server->getGroupManager()->cleanupAssignments(this->getClientDatabaseId());

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "ConnectedClient.h" #include "ConnectedClient.h"
#include "./shared/WhisperHandler.h"
#include <json/json.h> #include <json/json.h>
#include <src/rtc/lib.h> #include <src/rtc/lib.h>
@ -35,19 +36,20 @@ namespace ts::server {
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b); SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b);
~SpeakingClient() override; ~SpeakingClient() override;
//Voice /* TODO: Remove this method. Currently only the music but uses that to broadcast his audio */
virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
virtual bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender); bool should_handle_voice_packet(size_t /* size */);
//Whisper virtual bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender);
bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &sender); bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &sender);
virtual void send_voice_whisper_packet(const pipes::buffer_view& /* teamspeak payload */, const pipes::buffer_view& /* teaspeak payload */, const VoicePacketFlags& /* flags */) = 0;
inline std::chrono::milliseconds takeSpokenTime() { inline std::chrono::milliseconds takeSpokenTime() {
auto time = this->speak_time; auto time = this->speak_time;
this->speak_time = std::chrono::milliseconds(0); this->speak_time = std::chrono::milliseconds(0);
return time; return time;
} }
[[nodiscard]] inline whisper::WhisperHandler& whisper_handler() { return this->whisper_handler_; }
protected: protected:
void tick(const std::chrono::system_clock::time_point &time) override; void tick(const std::chrono::system_clock::time_point &time) override;
@ -58,9 +60,6 @@ namespace ts::server {
command_result handleCommand(Command &command) override; command_result handleCommand(Command &command) override;
public: public:
bool should_handle_voice_packet(size_t /* size */);
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */, bool /* head */);
virtual void processJoin(); virtual void processJoin();
void processLeave(); void processLeave();
@ -97,6 +96,8 @@ namespace ts::server {
std::shared_ptr<Json::Value> identityData; std::shared_ptr<Json::Value> identityData;
} handshake; } handshake;
whisper::WhisperHandler whisper_handler_;
bool rtc_session_pending_describe{false}; bool rtc_session_pending_describe{false};
rtc::RTCClientId rtc_client_id{0}; rtc::RTCClientId rtc_client_id{0};
}; };

View File

@ -383,10 +383,12 @@ void MusicClient::broadcast_music_stop() {
SpeakingClient::VoicePacketFlags flags{}; SpeakingClient::VoicePacketFlags flags{};
for(const auto& cl : this->server->getClientsByChannel<SpeakingClient>(this->currentChannel)) for(const auto& cl : this->server->getClientsByChannel<SpeakingClient>(this->currentChannel)) {
if(cl->shouldReceiveVoice(_this.lock())) if(cl->shouldReceiveVoice(_this.lock())) {
cl->send_voice_packet(pipes::buffer_view{voice_buffer, voice_header_length}, flags); cl->send_voice_packet(pipes::buffer_view{voice_buffer, voice_header_length}, flags);
} }
}
}
void MusicClient::execute_music_tick(const shared_ptr<ts::music::PlayableSong>& song) { void MusicClient::execute_music_tick(const shared_ptr<ts::music::PlayableSong>& song) {
unique_lock song_lock(this->current_song_lock); unique_lock song_lock(this->current_song_lock);

View File

@ -0,0 +1,364 @@
//
// Created by WolverinDEV on 26/11/2020.
//
#include "WhisperHandler.h"
#include "src/client/voice/VoiceClientConnection.h"
#include <misc/endianness.h>
using namespace ts::server::whisper;
constexpr static auto kMaxWhisperTargets{1024};
WhisperHandler::WhisperHandler(SpeakingClient* handle) : handle{handle} {};
WhisperHandler::~WhisperHandler() {
if(this->whisper_head_ptr) {
::free(this->whisper_head_ptr);
}
}
bool WhisperHandler::validate_whisper_packet(const protocol::ClientPacketParser &packet, bool& match_last_header, void *&payload_ptr, size_t &payload_length) {
size_t head_length;
if(packet.flags() & protocol::PacketFlag::NewProtocol) {
if(packet.payload_length() < 3 + 10) {
/* packet too short */
return false;
}
head_length = 10;
} else {
if(packet.payload_length() < 3 + 2) {
/* packet too short */
return false;
}
auto channel_count = (uint8_t) packet.payload()[3];
auto client_count = (uint8_t) packet.payload()[4];
head_length = 2 + channel_count * 8 + client_count * 2;
if(packet.payload_length() < 3 + head_length) {
/* packet is too short */
return false;
}
}
auto head_ptr = packet.payload().data_ptr<uint8_t>() + 3;
match_last_header = this->whisper_head_length == head_length && memcmp(this->whisper_head_ptr, head_ptr, head_length) == 0;
if(!match_last_header) {
if(this->whisper_head_capacity < head_length) {
if(this->whisper_head_ptr) {
::free(this->whisper_head_ptr);
}
this->whisper_head_ptr = malloc(head_length);
this->whisper_head_capacity = head_length;
}
this->whisper_head_length = head_length;
memcpy(this->whisper_head_ptr, head_ptr, head_length);
}
assert(packet.payload_length() >= head_length + 3);
payload_ptr = (void*) (head_ptr + head_length);
payload_length = packet.payload_length() - head_length - 3;
return true;
}
bool WhisperHandler::process_packet(const protocol::ClientPacketParser &packet, void *&payload_ptr, size_t &payload_length) {
bool match_last_header;
if(!this->validate_whisper_packet(packet, match_last_header, payload_ptr, payload_length)) {
return false;
}
auto current_timestamp = std::chrono::system_clock::now();
switch (this->session_state) {
case SessionState::Uninitialized:
/* Definitively initialize a new session */
break;
case SessionState::InitializeFailed: {
if(!match_last_header) {
/* Last header does not matches the current header, we need to reinitialize the session */
break;
}
if(current_timestamp - std::chrono::milliseconds{500} < this->session_timestamp) {
/* Last initialize failed and is less than 500ms ago */
return false;
}
/* Lets try to initialize the whisper session again */
break;
}
case SessionState::Initialized:
if(!match_last_header) {
/* Last header does not matches the current header, we need to reinitialize the session */
break;
}
if(current_timestamp - std::chrono::seconds{5} > this->session_timestamp) {
/* Last session member update is 5 seconds ago. Updating the session again */
break;
}
/* We've nothing to change and everything is good */
return true;
}
this->session_timestamp = current_timestamp;
auto head_ptr = packet.payload().data_ptr<uint8_t>();
size_t head_offset{3}; /* the first three bytes contain the voice sequence id and codec */
if(packet.flags() & protocol::PacketFlag::NewProtocol) {
auto type = head_ptr[head_offset++];
auto target = head_ptr[head_offset++];
auto type_id = be2le64(head_ptr, head_offset, &head_offset);
auto result = this->initialize_session_new(2, type, target, type_id);
if(result.has_error()) {
this->session_state = SessionState::InitializeFailed;
this->handle->notifyError(result);
} else {
this->session_state = SessionState::Initialized;
}
result.release_data();
} else {
auto channel_count = (uint8_t) head_ptr[head_offset++];
auto client_count = (uint8_t) head_ptr[head_offset++];
ChannelId channel_ids[channel_count];
ClientId client_ids[client_count];
for(uint8_t index = 0; index < channel_count; index++) {
channel_ids[index] = be2le64(head_ptr, head_offset, &head_offset);
}
for(uint8_t index = 0; index < client_count; index++) {
client_ids[index] = be2le16(head_ptr, head_offset, &head_offset);
}
auto result = this->initialize_session_old(2, client_ids, client_count, channel_ids, channel_count);
if(result.has_error()) {
this->session_state = SessionState::InitializeFailed;
this->handle->notifyError(result);
} else {
this->session_state = SessionState::Initialized;
}
result.release_data();
}
return this->session_state == SessionState::Initialized;
}
void WhisperHandler::handle_session_reset() {
this->session_state = SessionState::Uninitialized;
if(this->whisper_head_ptr) {
::free(this->whisper_head_ptr);
this->whisper_head_capacity = 0;
this->whisper_head_length = 0;
}
}
size_t WhisperHandler::max_whisper_targets() {
auto server = this->handle->getServer();
if(!server) {
return false;
}
auto result = server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>([]{ return kMaxWhisperTargets; });
if(result > kMaxWhisperTargets) {
result = kMaxWhisperTargets;
}
return result;
}
ts::command_result WhisperHandler::initialize_session_old(uint32_t stream_id, const uint16_t *client_ids, size_t client_count, const uint64_t *channel_ids, size_t channel_count) {
auto server = this->handle->getServer();
if(!server) {
return ts::command_result{error::vs_critical, "missing server"};
}
std::vector<std::shared_ptr<SpeakingClient>> target_clients{};
auto connected_clients = server->getClients();
target_clients.reserve(connected_clients.size());
for(const auto& connected_client : connected_clients) {
auto speaking_client = dynamic_pointer_cast<SpeakingClient>(connected_client);
if(!speaking_client || speaking_client == this->handle || !speaking_client->rtc_client_id) {
continue;
}
auto client_channel_id = speaking_client->getChannelId();
auto client_id = speaking_client->getClientId();
for(size_t index = 0; index < channel_count; index++) {
if(channel_ids[index] == client_channel_id) {
goto add_client;
}
}
for(size_t index = 0; index < client_count; index++) {
if(client_ids[index] == client_id) {
goto add_client;
}
}
continue;
add_client:
target_clients.push_back(speaking_client);
}
return this->configure_rtc_clients(stream_id, target_clients);
}
ts::command_result WhisperHandler::initialize_session_new(uint32_t stream_id, uint8_t type_u8, uint8_t target_u8, uint64_t type_id) {
auto type = (WhisperType) type_u8;
auto target = (WhisperTarget) target_u8;
auto server = this->handle->getServer();
if(!server) {
return ts::command_result{error::vs_critical, "missing server"};
}
#ifdef PKT_LOG_WHISPER
logTrace(this->getServerId(), "{} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}.", CLIENT_STR_LOG_PREFIX, data_length, type, target, type_id);
#endif
std::vector<std::shared_ptr<SpeakingClient>> target_clients{};
if(type == WhisperType::ECHO) {
target_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->handle->ref()));
} else {
auto connected_clients = server->getClients();
target_clients.reserve(connected_clients.size());
for (const auto &available_client : connected_clients) {
auto speaking_client = dynamic_pointer_cast<SpeakingClient>(available_client);
if (!speaking_client || this->handle == speaking_client || !speaking_client->rtc_client_id) {
continue;
}
if (type == WhisperType::ALL) {
target_clients.push_back(speaking_client);
} else if (type == WhisperType::SERVER_GROUP) {
if (type_id == 0) {
target_clients.push_back(speaking_client);
} else {
std::shared_lock client_view_lock(speaking_client->get_channel_lock());
for (const auto &id : speaking_client->current_server_groups()) {
if (id == type_id) {
target_clients.push_back(speaking_client);
break;
}
}
}
} else if (type == WhisperType::CHANNEL_GROUP) {
if (speaking_client->current_channel_group() == type_id) {
target_clients.push_back(speaking_client);
}
} else if (type == WhisperType::CHANNEL_COMMANDER) {
if (speaking_client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as_save<bool>([] { return false; })) {
target_clients.push_back(speaking_client);
}
} else {
return ts::command_result{error::parameter_invalid, "type"};
}
}
if (target == WhisperTarget::CHANNEL_CURRENT) {
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient> &target) {
return target->getChannel() != this->handle->getChannel();
}),
target_clients.end());
} else if (target == WhisperTarget::CHANNEL_PARENT) {
auto current_parent = this->handle->getChannel();
if (current_parent && (current_parent = current_parent->parent())) {
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient> &target) {
return target->getChannel() != current_parent;
}),
target_clients.end());
} else {
target_clients.clear();
}
} else if (target == WhisperTarget::CHANNEL_ALL_PARENT) {
auto current_parent = this->handle->getChannel();
if (current_parent && (current_parent = current_parent->parent())) {
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient> &target) {
auto tmp_parent = current_parent;
while (tmp_parent && tmp_parent != target->getChannel())
tmp_parent = tmp_parent->parent();
return target->getChannel() != tmp_parent;
}),
target_clients.end());
} else {
target_clients.clear();
}
} else if (target == WhisperTarget::CHANNEL_FAMILY) {
auto current_channel = this->handle->getChannel();
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient> &target) {
auto tmp_current = target->getChannel();
while (tmp_current && tmp_current != current_channel) {
tmp_current = tmp_current->parent();
}
return current_channel != tmp_current;
}),
target_clients.end());
} else if (target == WhisperTarget::CHANNEL_COMPLETE_FAMILY) {
shared_ptr<BasicChannel> current = this->handle->getChannel();
while (current && current->parent()) current = current->parent();
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient> &target) {
auto tmp_current = target->getChannel();
while (tmp_current && tmp_current != current)
tmp_current = tmp_current->parent();
return current != tmp_current;
}),
target_clients.end());
} else if (target == WhisperTarget::CHANNEL_SUBCHANNELS) {
shared_ptr<BasicChannel> current = this->handle->getChannel();
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<SpeakingClient> &target) {
return target->getChannel()->parent() != current;
}),
target_clients.end());
} else if(target == WhisperTarget::CHANNEL_ALL) {
/* nothing to filter out */
} else {
return ts::command_result{error::parameter_invalid, "target"};
}
}
return this->configure_rtc_clients(stream_id, target_clients);
}
ts::command_result WhisperHandler::configure_rtc_clients(uint32_t stream_id, const std::vector<std::shared_ptr<SpeakingClient>>& target_clients) {
auto max_clients = this->max_whisper_targets();
assert(max_clients <= kMaxWhisperTargets);
if(target_clients.size() >= max_clients) {
return ts::command_result{error::whisper_too_many_targets};
}
if(target_clients.empty()) {
return ts::command_result{error::whisper_no_targets};
}
auto server = this->handle->getServer();
if(!server) {
return ts::command_result{error::vs_critical, "missing server"};
}
uint32_t target_client_ids[kMaxWhisperTargets];
size_t target_client_count{0};
for(const auto& target_client : target_clients) {
target_client_ids[target_client_count++] = target_client->rtc_client_id;
}
std::string error;
if(!server->rtc_server().configure_whisper_session(error, this->handle->rtc_client_id, stream_id, target_client_ids, target_client_count)) {
logCritical(server->getServerId(), "{} Failed to configure whisper session with {} participants.", CLIENT_STR_LOG_PREFIX_(this->handle), target_client_count);
return ts::command_result{error::vs_critical, error};
}
logTrace(server->getServerId(), "{} Configured whisper session with {} participants.", CLIENT_STR_LOG_PREFIX_(this->handle), target_client_count);
return ts::command_result{error::ok};
}

View File

@ -0,0 +1,76 @@
#pragma once
#include <Error.h>
#include <protocol/Packet.h>
#include <string_view>
namespace ts::connection {
class VoiceClientConnection;
}
namespace ts::server {
class VoiceClient;
class SpeakingClient;
}
namespace ts::server::whisper {
enum struct WhisperType {
SERVER_GROUP = 0,
CHANNEL_GROUP = 1,
CHANNEL_COMMANDER = 2,
ALL = 3,
ECHO = 0x10,
};
enum struct WhisperTarget {
CHANNEL_ALL = 0,
CHANNEL_CURRENT = 1,
CHANNEL_PARENT = 2,
CHANNEL_ALL_PARENT = 3,
CHANNEL_FAMILY = 4,
CHANNEL_COMPLETE_FAMILY = 5,
CHANNEL_SUBCHANNELS = 6
};
class WhisperHandler {
public:
explicit WhisperHandler(SpeakingClient* /* handle */);
~WhisperHandler();
/*
* Preprocess a whisper packet.
* If the result is false the packet should not be send into the rtc source pipe.
*/
bool process_packet(const protocol::ClientPacketParser& /* packet */, void*& /* payload ptr */, size_t& /* payload length */);
ts::command_result initialize_session_new(uint32_t /* stream id */, uint8_t /* type */, uint8_t /* target */, uint64_t /* type id */);
ts::command_result initialize_session_old(uint32_t /* stream id */, const uint16_t* /* client ids */, size_t /* client count */, const uint64_t* /* channel ids */, size_t /* channel count */);
void handle_session_reset();
private:
enum struct SessionState {
Uninitialized,
InitializeFailed,
Initialized
};
SpeakingClient* handle;
SessionState session_state{SessionState::Uninitialized};
std::chrono::system_clock::time_point session_timestamp{};
void* whisper_head_ptr{nullptr};
size_t whisper_head_length{0};
size_t whisper_head_capacity{0};
/**
* This also updates the last head ptr.
* @return True if the packet is a valid whisper packet. The payload ptr and payload length variables will be set
*/
bool validate_whisper_packet(const protocol::ClientPacketParser& /* packet */, bool& /* matches last header */, void*& /* payload ptr */, size_t& /* payload length */);
ts::command_result configure_rtc_clients(uint32_t /* stream id */, const std::vector<std::shared_ptr<SpeakingClient>>& /* clients */);
[[nodiscard]] size_t max_whisper_targets();
};
}

View File

@ -16,6 +16,10 @@ using namespace std::chrono;
using namespace ts::server; using namespace ts::server;
using namespace ts::protocol; using namespace ts::protocol;
constexpr static auto kMaxWhisperClientNameLength{30};
constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */
constexpr static auto kWhisperMaxHeaderLength{2 + 2 + 1 + 2 + kWhisperClientUniqueIdLength + 1 + kMaxWhisperClientNameLength};
VoiceClient::VoiceClient(const std::shared_ptr<VoiceServer>& server, const sockaddr_storage* address) : SpeakingClient(server->server->sql, server->server), voice_server(server) { VoiceClient::VoiceClient(const std::shared_ptr<VoiceServer>& server, const sockaddr_storage* address) : SpeakingClient(server->server->sql, server->server), voice_server(server) {
assert(address); assert(address);
memtrack::allocated<VoiceClient>(this); memtrack::allocated<VoiceClient>(this);
@ -255,18 +259,84 @@ void VoiceClient::send_voice_packet(const pipes::buffer_view &voice_buffer, cons
this->connection->send_packet(PacketType::VOICE, packet_flags, voice_buffer.data_ptr<void>(), voice_buffer.length()); this->connection->send_packet(PacketType::VOICE, packet_flags, voice_buffer.data_ptr<void>(), voice_buffer.length());
} }
void VoiceClient::send_voice_whisper_packet(const pipes::buffer_view &teamspeak_packet, const pipes::buffer_view &teaspeak_packet, const SpeakingClient::VoicePacketFlags &flags) { void VoiceClient::send_voice(const std::shared_ptr<SpeakingClient> &source_client, uint16_t seq_no, uint8_t codec, const void *payload, size_t payload_length) {
PacketFlag::PacketFlags packet_flags{PacketFlag::None}; /* TODO: Somehow set the head (compressed) flag for beginning voice packets? */
packet_flags |= flags.encrypted ? 0U : PacketFlag::Unencrypted; auto packet = protocol::allocate_outgoing_packet(payload_length + 5);
packet_flags |= flags.head ? PacketFlag::Compressed : 0U; packet->type_and_flags = protocol::PacketType::VOICE;
packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0U;
packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0U;
if(this->getType() == ClientType::CLIENT_TEASPEAK) { *((uint16_t*) packet->payload + 0) = htons(seq_no);
this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, teaspeak_packet.data_ptr<void>(), teaspeak_packet.length()); *((uint16_t*) packet->payload + 1) = htons(source_client->getClientId());
packet->payload[4] = codec;
if(payload) {
memcpy(packet->payload + 5, payload, payload_length);
} else { } else {
this->connection->send_packet(PacketType::VOICE_WHISPER, packet_flags, teamspeak_packet.data_ptr<void>(), teamspeak_packet.length()); assert(payload_length == 0);
} }
this->getConnection()->send_packet(packet);
}
void VoiceClient::send_voice_whisper(const std::shared_ptr<SpeakingClient> &source_client, uint16_t seq_no, uint8_t codec, const void *payload, size_t payload_length) {
bool head{false};
if(this->whisper_head_counter < 5) {
head = true;
this->whisper_head_counter++;
}
if(!payload) {
this->whisper_head_counter = 0;
}
protocol::OutgoingServerPacket* packet;
size_t payload_offset{0};
if(head && this->getType() == ClientType::CLIENT_TEASPEAK) {
auto uniqueId = source_client->getUid();
auto nickname = source_client->getDisplayName();
if(uniqueId.length() > kWhisperClientUniqueIdLength) {
logCritical(LOG_GENERAL, "Clients unique id is longer than the expected max length of {}. Unique length: {}", kWhisperClientUniqueIdLength, uniqueId.length());
return;
}
if(nickname.length() > kMaxWhisperClientNameLength) {
logCritical(LOG_GENERAL, "Clients name is longer than the expected max length of {}. Name length: {}", kMaxWhisperClientNameLength, nickname.length());
return;
}
packet = protocol::allocate_outgoing_packet(payload_length + 5 + kWhisperClientUniqueIdLength + 1 + nickname.length());
packet->type_and_flags |= protocol::PacketFlag::Compressed;
memset(packet->payload + payload_offset, 0, kWhisperClientUniqueIdLength);
memcpy(packet->payload + payload_offset, uniqueId.data(), uniqueId.length());
payload_offset += kWhisperClientUniqueIdLength;
packet->payload[payload_offset++] = nickname.length();
memcpy(packet->payload + payload_offset, nickname.data(), nickname.length());
payload_offset += nickname.length();
} else {
packet = protocol::allocate_outgoing_packet(payload_length + 5);
}
packet->type_and_flags |= protocol::PacketType::VOICE_WHISPER;
*((uint16_t*) &packet->payload[payload_offset]) = htons(seq_no);
payload_offset += 2;
*((uint16_t*) &packet->payload[payload_offset]) = htons(source_client->getClientId());
payload_offset += 2;
packet->payload[payload_offset] = codec;
payload_offset += 1;
if(payload) {
memcpy(packet->payload + payload_offset, payload, payload_length);
payload_offset += payload_length;
} else {
assert(payload_length == 0);
}
packet->payload_size = payload_offset;
this->getConnection()->send_packet(packet);
} }
float VoiceClient::current_ping_deviation() { float VoiceClient::current_ping_deviation() {
@ -279,8 +349,18 @@ float VoiceClient::current_packet_loss() const {
void VoiceClient::processJoin() { void VoiceClient::processJoin() {
SpeakingClient::processJoin(); SpeakingClient::processJoin();
if(this->rtc_client_id > 0) { if(this->rtc_client_id > 0) {
auto sender = this->server->rtc_server().create_audio_source_supplier_sender(this->rtc_client_id); {
/* Normal audio channel */
auto sender = this->server->rtc_server().create_audio_source_supplier_sender(this->rtc_client_id, 1);
assert(sender.has_value()); assert(sender.has_value());
this->rtc_audio_supplier.reset(*sender); this->rtc_audio_supplier.reset(*sender);
} }
{
/* Audio whisper channel */
auto sender = this->server->rtc_server().create_audio_source_supplier_sender(this->rtc_client_id, 2);
assert(sender.has_value());
this->rtc_audio_whisper_supplier.reset(*sender);
}
}
} }

View File

@ -90,11 +90,8 @@ namespace ts {
void handlePacketCommand(const pipes::buffer_view&); void handlePacketCommand(const pipes::buffer_view&);
public: public:
void send_voice_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override; void send_voice_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override;
void send_voice_whisper_packet( void send_voice(const std::shared_ptr<SpeakingClient>& /* source client */, uint16_t /* seq no */, uint8_t /* codec */, const void* /* payload */, size_t /* payload length */);
const pipes::buffer_view &/* teamspeak packet */, void send_voice_whisper(const std::shared_ptr<SpeakingClient>& /* source client */, uint16_t /* seq no */, uint8_t /* codec */, const void* /* payload */, size_t /* payload length */);
const pipes::buffer_view &/* teaspeak packet */,
const VoicePacketFlags &flags
) override;
void processJoin() override; void processJoin() override;
protected: protected:
@ -105,7 +102,10 @@ namespace ts {
bool final_disconnected = false; bool final_disconnected = false;
rtc::NativeAudioSourceSupplier rtc_audio_supplier{}; rtc::NativeAudioSourceSupplier rtc_audio_supplier{};
rtc::NativeAudioSourceSupplier rtc_audio_whisper_supplier{};
uint16_t stop_seq_counter{0}; uint16_t stop_seq_counter{0};
uint16_t whisper_head_counter{0};
//General TS3 manager commands //General TS3 manager commands
command_result handleCommandClientInit(Command&) override; command_result handleCommandClientInit(Command&) override;

View File

@ -1,26 +1,27 @@
#pragma once #pragma once
#include <protocol/ringbuffer.h>
#include <protocol/CompressionHandler.h>
#include <protocol/CryptHandler.h>
#include <ThreadPool/Thread.h>
#include <ThreadPool/Mutex.h>
#include <protocol/buffers.h>
#include <chrono>
#include <deque>
#include <event.h>
#include <condition_variable>
#include <utility>
#include <pipes/buffer.h>
#include "VoiceClient.h"
#include "protocol/AcknowledgeManager.h"
#include <protocol/generation.h>
#include "./PacketStatistics.h"
#include "./PacketDecoder.h" #include "./PacketDecoder.h"
#include "./PacketEncoder.h" #include "./PacketEncoder.h"
#include "./PacketStatistics.h"
#include "./ServerCommandExecutor.h" #include "./ServerCommandExecutor.h"
#include "CryptSetupHandler.h" #include "CryptSetupHandler.h"
#include "PingHandler.h" #include "PingHandler.h"
#include "VoiceClient.h"
#include "protocol/AcknowledgeManager.h"
#include "src/client/shared/WhisperHandler.h"
#include <ThreadPool/Mutex.h>
#include <ThreadPool/Thread.h>
#include <chrono>
#include <condition_variable>
#include <deque>
#include <event.h>
#include <pipes/buffer.h>
#include <protocol/CompressionHandler.h>
#include <protocol/CryptHandler.h>
#include <protocol/buffers.h>
#include <protocol/generation.h>
#include <protocol/ringbuffer.h>
#include <utility>
//#define LOG_ACK_SYSTEM //#define LOG_ACK_SYSTEM
#ifdef LOG_ACK_SYSTEM #ifdef LOG_ACK_SYSTEM

View File

@ -37,13 +37,31 @@ void VoiceClientConnection::handlePacketVoice(const protocol::ClientPacketParser
sink.send_audio(vpacketId, false, vpacketId * 960, codec, std::string_view{payload.data_ptr<char>() + 3, payload.length() - 3}); sink.send_audio(vpacketId, false, vpacketId * 960, codec, std::string_view{payload.data_ptr<char>() + 3, payload.length() - 3});
} }
client->resetIdleTime();
client->updateSpeak(false, std::chrono::system_clock::now());
} }
void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) { void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) {
auto client = this->getCurrentClient(); auto client = this->getCurrentClient();
if(!client) return; if(!client) return;
client->handlePacketVoiceWhisper(packet.payload(), (packet.flags() & PacketFlag::NewProtocol) > 0, (packet.flags() & PacketFlag::Compressed) > 0); void* payload;
size_t payload_length;
if(!client->whisper_handler().process_packet(packet, payload, payload_length)) {
/* packet invalid or session failed to initialize */
return;
}
auto voice_packet_id = ntohs(*packet.payload().data_ptr<uint16_t>());
auto voice_codec = packet.payload().data_ptr<uint8_t>()[2];
auto& sink = client->rtc_audio_whisper_supplier;
sink.send_audio(voice_packet_id, false, voice_packet_id * 960, voice_codec, std::string_view{(const char*) payload, payload_length});
client->resetIdleTime();
client->updateSpeak(false, std::chrono::system_clock::now());
} }
void VoiceClientConnection::handlePacketAck(const protocol::ClientPacketParser& packet) { void VoiceClientConnection::handlePacketAck(const protocol::ClientPacketParser& packet) {

View File

@ -265,10 +265,11 @@ command_result WebClient::handleCommand(Command &command) {
return result; return result;
} }
} }
if(command.command() == "setwhispertarget") {
return this->handleCommandSetWhisperTarget(command); if(command.command() == "whispersessioninitialize") {
} else if(command.command() == "clearwhispertarget") { return this->handleCommandWhisperSessionInitialize(command);
return this->handleCommandClearWhisperTarget(command); } else if(command.command() == "whispersessionreset") {
return this->handleCommandWhisperSessionReset(command);
} }
return SpeakingClient::handleCommand(command); return SpeakingClient::handleCommand(command);
} }
@ -528,11 +529,7 @@ void WebClient::send_voice_packet(const pipes::buffer_view &view, const Speaking
/* Should never be called! */ /* Should never be called! */
} }
void WebClient::send_voice_whisper_packet(const pipes::buffer_view &, const pipes::buffer_view &teaspeak_packet, const SpeakingClient::VoicePacketFlags &flags) { command_result WebClient::handleCommandWhisperSessionInitialize(Command &command) {
/* Should never be called! */
}
command_result WebClient::handleCommandSetWhisperTarget(Command &command) {
auto server = this->getServer(); auto server = this->getServer();
if(!server) { if(!server) {
return command_result{error::server_unbound}; return command_result{error::server_unbound};
@ -620,7 +617,7 @@ command_result WebClient::handleCommandSetWhisperTarget(Command &command) {
return command_result{error::ok}; return command_result{error::ok};
} }
command_result WebClient::handleCommandClearWhisperTarget(Command &command) { command_result WebClient::handleCommandWhisperSessionReset(Command &command) {
std::lock_guard whisper_buffer_lock{this->whisper.mutex}; std::lock_guard whisper_buffer_lock{this->whisper.mutex};
this->whisper.is_set = false; this->whisper.is_set = false;
return command_result{error::ok}; return command_result{error::ok};

View File

@ -105,15 +105,14 @@ namespace ts::server {
public: public:
void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override;
void send_voice_whisper_packet(const pipes::buffer_view &/* teamspeak packet */, const pipes::buffer_view &/* teaspeak packet */, const VoicePacketFlags &flags) override;
protected: protected:
command_result handleCommand(Command &command) override; command_result handleCommand(Command &command) override;
command_result handleCommandClientInit(Command &command) override; command_result handleCommandClientInit(Command &command) override;
command_result handleCommandSetWhisperTarget(Command &command); command_result handleCommandWhisperSessionInitialize(Command &command);
command_result handleCommandClearWhisperTarget(Command &command); command_result handleCommandWhisperSessionReset(Command &command);
}; };
} }
#endif #endif

View File

@ -21,6 +21,8 @@ struct NativeCallbacks {
void(*client_stream_stop)(const void* /* callback data */, uint32_t /* stream id */, const void* /* source callback data */); void(*client_stream_stop)(const void* /* callback data */, uint32_t /* stream id */, const void* /* source callback data */);
void(*client_audio_sender_data)(const void* /* callback data */, const void* /* source callback data */, uint8_t /* mode */, uint16_t /* seq. no. */, uint8_t /* codec */, const void* /* data */, uint32_t /* length */); void(*client_audio_sender_data)(const void* /* callback data */, const void* /* source callback data */, uint8_t /* mode */, uint16_t /* seq. no. */, uint8_t /* codec */, const void* /* data */, uint32_t /* length */);
void(*client_whisper_session_reset)(const void* /* callback data */);
}; };
struct RtpClientConfigureOptions { struct RtpClientConfigureOptions {
@ -39,22 +41,23 @@ extern const char* librtc_version();
extern void librtc_free_str(const char* /* ptr */); extern void librtc_free_str(const char* /* ptr */);
extern const char* librtc_init(const NativeCallbacks* /* */, size_t /* size of the callback struct */); extern const char* librtc_init(const NativeCallbacks* /* */, size_t /* size of the callback struct */);
extern const char* librtc_rtc_configure(void* /* callback data */, const RtpClientConfigureOptions* /* config */, size_t /* config size */);
extern void* librtc_create_server(); extern void* librtc_create_server();
extern void librtc_destroy_server(void* /* server */); extern void librtc_destroy_server(void* /* server */);
extern uint32_t librtc_create_rtp_client(void* /* server */, void* /* callback data */, const char** /* error ptr */); extern uint32_t librtc_create_client(void* /* server */, void* /* callback data */);
extern uint32_t librtc_create_native_client(void* /* server */, void* /* callback data */);
extern void librtc_destroy_client(void* /* server */, uint32_t /* client id */); extern void librtc_destroy_client(void* /* server */, uint32_t /* client id */);
extern const char* librtc_rtc_configure(void* /* callback data */, const RtpClientConfigureOptions* /* config */, size_t /* config size */); extern const char* librtc_initialize_rtc_connection(void* /* server */, uint32_t /* client id */);
extern const char* librtc_initialize_native_connection(void* /* server */, uint32_t /* client id */);
extern const char* librtc_reset_rtp_session(void* /* server */, uint32_t /* client id */); extern const char* librtc_reset_rtp_session(void* /* server */, uint32_t /* client id */);
extern const char* librtc_apply_remote_description(void* /* server */, uint32_t /* client id */, uint32_t /* mode */, const char* /* description */); extern const char* librtc_apply_remote_description(void* /* server */, uint32_t /* client id */, uint32_t /* mode */, const char* /* description */);
extern const char* librtc_generate_local_description(void* /* server */, uint32_t /* client id */, char** /* description */); extern const char* librtc_generate_local_description(void* /* server */, uint32_t /* client id */, char** /* description */);
extern const char* librtc_add_ice_candidate(void* /* server */, uint32_t /* client id */, uint32_t /* media line */, const char* /* candidate */); extern const char* librtc_add_ice_candidate(void* /* server */, uint32_t /* client id */, uint32_t /* media line */, const char* /* candidate */);
extern void* librtc_create_audio_source_supplier(void* /* server */, uint32_t /* client id */); extern void* librtc_create_audio_source_supplier(void* /* server */, uint32_t /* client id */, uint32_t /* stream id */);
extern void librtc_audio_source_supply(void* /* sender */, extern void librtc_audio_source_supply(void* /* sender */,
uint16_t /* seq no */, uint16_t /* seq no */,
bool /* marked */, bool /* marked */,
@ -69,6 +72,8 @@ extern uint32_t librtc_assign_channel(void* /* server */, uint32_t /* client id
extern uint32_t librtc_client_broadcast(void* /* server */, uint32_t /* client id */, uint8_t /* broadcast type */, uint32_t /* stream id */); extern uint32_t librtc_client_broadcast(void* /* server */, uint32_t /* client id */, uint8_t /* broadcast type */, uint32_t /* stream id */);
extern void librtc_destroy_channel(void* /* server */, uint32_t /* channel */); extern void librtc_destroy_channel(void* /* server */, uint32_t /* channel */);
extern const char* librtc_whisper_configure(void* /* server */, uint32_t /* client id */, uint32_t /* source stream id */, uint32_t* /* client ids */, uint32_t /* client id count */);
#ifdef __cplusplus #ifdef __cplusplus
}; };
#endif #endif

View File

@ -187,31 +187,32 @@ void librtc_callback_client_audio_sender_data(const void* callback_data_ptr, con
return; return;
} }
/* TODO: Somehow set the head (compressed) flag for beginning voice packets? */ target_client->send_voice(source_client, seq_no, codec, data, length);
auto packet = protocol::allocate_outgoing_packet(length + 5); } else if(mode == 1) {
packet->type_and_flags = protocol::PacketType::VOICE;
*((uint16_t*) packet->payload + 0) = htons(seq_no);
*((uint16_t*) packet->payload + 1) = htons(source_data->client_id);
packet->payload[4] = codec;
if(data) {
memcpy(packet->payload + 5, data, length);
} else {
assert(length == 0);
}
target_client->getConnection()->send_packet(packet);
} else {
if(!target_client->shouldReceiveVoiceWhisper(source_client)) { if(!target_client->shouldReceiveVoiceWhisper(source_client)) {
return; return;
} }
/* FIXME: TODO! */ target_client->send_voice_whisper(source_client, seq_no, codec, data, length);
} else {
/* we've received audio with an invalid mode.... */
} }
} }
void librtc_client_whisper_session_reset(const void* callback_data_ptr) {
auto callback_data = (LibCallbackData*) callback_data_ptr;
auto client = std::dynamic_pointer_cast<SpeakingClient>(callback_data->weak_ref.lock());
if(!client) { return; }
client->whisper_handler().handle_session_reset();
ts::command_builder notify{"notifywhispersessionreset"};
client->sendCommand(notify);
}
static NativeCallbacks native_callbacks{ static NativeCallbacks native_callbacks{
.version = 3, .version = 4,
.log = librtc_callback_log, .log = librtc_callback_log,
.free_client_data = librtc_callback_free_client_data, .free_client_data = librtc_callback_free_client_data,
@ -224,7 +225,8 @@ static NativeCallbacks native_callbacks{
.client_stream_start = librtc_callback_client_audio_start, .client_stream_start = librtc_callback_client_audio_start,
.client_stream_stop = librtc_callback_client_audio_stop, .client_stream_stop = librtc_callback_client_audio_stop,
.client_audio_sender_data = librtc_callback_client_audio_sender_data .client_audio_sender_data = librtc_callback_client_audio_sender_data,
.client_whisper_session_reset = librtc_client_whisper_session_reset
}; };
std::string_view rtc::version() { std::string_view rtc::version() {
@ -248,36 +250,31 @@ Server::~Server() {
librtc_destroy_server(this->server_ptr); librtc_destroy_server(this->server_ptr);
} }
RTCClientId Server::create_rtp_client(const std::shared_ptr<server::SpeakingClient> &client, std::string& error) { RTCClientId Server::create_client(const std::shared_ptr<server::SpeakingClient> &client) {
auto data = new LibCallbackData{ auto data = new LibCallbackData{
.client_id = client->getClientId(), .client_id = client->getClientId(),
.weak_ref = client .weak_ref = client
}; };
const char* error_ptr{nullptr}; return librtc_create_client(this->server_ptr, data);
auto client_id = librtc_create_rtp_client(this->server_ptr, data, &error_ptr); }
if(client_id == 0) {
/* LibCallbackData will be automatically freed */
if(error_ptr) { bool Server::initialize_rtc_connection(std::string &error, RTCClientId client_id) {
error.clear(); auto error_ptr = librtc_initialize_rtc_connection(this->server_ptr, client_id);
error.append(error_ptr); if(!error_ptr) { return true; }
error = std::string{error_ptr};
librtc_free_str(error_ptr); librtc_free_str(error_ptr);
} else { return false;
error = "unknown error";
} }
return 0; bool Server::initialize_native_connection(std::string &error, RTCClientId client_id) {
} auto error_ptr = librtc_initialize_native_connection(this->server_ptr, client_id);
return client_id; if(!error_ptr) { return true; }
}
RTCClientId Server::create_native_client(const std::shared_ptr<server::SpeakingClient> &client) { error = std::string{error_ptr};
auto data = new LibCallbackData{ librtc_free_str(error_ptr);
.client_id = client->getClientId(), return false;
.weak_ref = client
};
return librtc_create_native_client(this->server_ptr, data);
} }
void Server::destroy_client(RTCClientId client_id) { void Server::destroy_client(RTCClientId client_id) {
@ -350,8 +347,8 @@ BroadcastStartResult Server::start_broadcast(uint32_t client_id, uint8_t btype,
} }
} }
std::optional<NativeAudioSourceSupplier> Server::create_audio_source_supplier_sender(uint32_t client_id) { std::optional<NativeAudioSourceSupplier> Server::create_audio_source_supplier_sender(uint32_t client_id, uint32_t stream_id) {
auto result = librtc_create_audio_source_supplier(this->server_ptr, client_id); auto result = librtc_create_audio_source_supplier(this->server_ptr, client_id, stream_id);
if(!result) { return std::nullopt; } if(!result) { return std::nullopt; }
return std::make_optional<NativeAudioSourceSupplier>(result); return std::make_optional<NativeAudioSourceSupplier>(result);
@ -361,6 +358,15 @@ void Server::destroy_channel(uint32_t channel_id) {
librtc_destroy_channel(this->server_ptr, channel_id); librtc_destroy_channel(this->server_ptr, channel_id);
} }
bool Server::configure_whisper_session(std::string &error, RTCClientId client_id, uint32_t source_stream_id, RTCClientId *client_ids, size_t client_id_count) {
auto error_ptr = librtc_whisper_configure(this->server_ptr, client_id, source_stream_id, client_ids, client_id_count);
if(!error_ptr) { return true; }
error = std::string{error_ptr};
librtc_free_str(error_ptr);
return false;
}
NativeAudioSourceSupplier::NativeAudioSourceSupplier(void *ptr) : sender_ptr{ptr} {} NativeAudioSourceSupplier::NativeAudioSourceSupplier(void *ptr) : sender_ptr{ptr} {}
NativeAudioSourceSupplier::NativeAudioSourceSupplier(NativeAudioSourceSupplier &&other) noexcept : sender_ptr{other.sender_ptr} { NativeAudioSourceSupplier::NativeAudioSourceSupplier(NativeAudioSourceSupplier &&other) noexcept : sender_ptr{other.sender_ptr} {
other.sender_ptr = nullptr; other.sender_ptr = nullptr;

View File

@ -38,8 +38,9 @@ namespace ts::rtc {
Server(); Server();
~Server(); ~Server();
RTCClientId create_rtp_client(const std::shared_ptr<server::SpeakingClient>& /* client */, std::string& /* error */); RTCClientId create_client(const std::shared_ptr<server::SpeakingClient>& /* client */);
RTCClientId create_native_client(const std::shared_ptr<server::SpeakingClient>& /* client */); bool initialize_rtc_connection(std::string& /* error */, RTCClientId /* client id */);
bool initialize_native_connection(std::string& /* error */, RTCClientId /* client id */);
void destroy_client(RTCClientId /* client id */); void destroy_client(RTCClientId /* client id */);
/* RTC client actions */ /* RTC client actions */
@ -50,13 +51,16 @@ namespace ts::rtc {
void ice_candidates_finished(RTCClientId /* client id */); void ice_candidates_finished(RTCClientId /* client id */);
/* Native client actions */ /* Native client actions */
std::optional<NativeAudioSourceSupplier> create_audio_source_supplier_sender(RTCClientId /* client id */); std::optional<NativeAudioSourceSupplier> create_audio_source_supplier_sender(RTCClientId /* client id */, uint32_t /* stream id */);
/* channel actions */ /* channel actions */
uint32_t create_channel(); uint32_t create_channel();
ChannelAssignResult assign_channel(RTCClientId /* client id */, RTCChannelId /* channel id */); ChannelAssignResult assign_channel(RTCClientId /* client id */, RTCChannelId /* channel id */);
BroadcastStartResult start_broadcast(RTCClientId /* client id */, uint8_t /* broadcast type */, RTCStreamId /* stream id */); BroadcastStartResult start_broadcast(RTCClientId /* client id */, uint8_t /* broadcast type */, RTCStreamId /* stream id */);
void destroy_channel(RTCChannelId /* channel id */); void destroy_channel(RTCChannelId /* channel id */);
/* whisper actions */
bool configure_whisper_session(std::string& /* error */, RTCClientId /* client id */, uint32_t /* source stream id */, RTCClientId* /* session members */, size_t /* session member count */);
private: private:
void* server_ptr{nullptr}; void* server_ptr{nullptr};
}; };

2
shared

@ -1 +1 @@
Subproject commit 23db0edd229a4cc37390bfc5df870d7aaf191f03 Subproject commit 2cc8a42ce7639efa2cdf6eb30a2a02f099ce1154