Initial video commit

This commit is contained in:
WolverinDEV 2020-11-07 13:17:51 +01:00
parent 6cd481e824
commit a37ba81a4f
46 changed files with 913 additions and 966 deletions

66
.clang-format Normal file
View File

@ -0,0 +1,66 @@
# Generated from CLion C/C++ Code Style settings
BasedOnStyle: LLVM
AccessModifierOffset: 0
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignOperands: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 8
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Right
ReflowComments: false
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never

3
.gitmodules vendored
View File

@ -8,3 +8,6 @@
[submodule "music"]
path = music
url = https://github.com/TeaSpeak/TeaMusic-Providers.git
[submodule "rtclib"]
path = rtclib
url = https://git.did.science/TeaSpeak/Server/rtc.git

@ -1 +1 @@
Subproject commit 3133288d211f5b85db59a390ae50e42e37cf0f18
Subproject commit d09428739c651d897ae5b00b394139891050428a

View File

@ -1,8 +0,0 @@
{
"name": "TeaSpeak",
"version": "1.0.0",
"dependencies": {
"@types/node": "^14.11.2",
"yaml": "^1.10.0"
}
}

1
rtclib Submodule

@ -0,0 +1 @@
Subproject commit 69a7427ed59bd2177671eabf0df999b98b008ae0

View File

@ -159,6 +159,8 @@ set(SERVER_SOURCE_FILES
src/server/voice/UDPVoiceServer.cpp
src/server/voice/DatagramPacket.cpp
src/rtc/lib.cpp
)
if (COMPILE_WEB_CLIENT)
@ -169,10 +171,7 @@ if (COMPILE_WEB_CLIENT)
src/server/WebServer.cpp
src/client/web/WebClient.cpp
# src/server/web/WebRTCServer.cpp
src/client/web/WSWebClient.cpp
src/client/web/SampleHandler.cpp
src/client/web/VoiceBridge.cpp
src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h)
endif ()
@ -256,8 +255,8 @@ target_link_libraries(PermMapHelper
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "4")
SET(CPACK_PACKAGE_VERSION_PATCH "22")
SET(CPACK_PACKAGE_VERSION_MINOR "5")
SET(CPACK_PACKAGE_VERSION_PATCH "0")
if (BUILD_TYPE_NAME EQUAL OFF)
SET(CPACK_PACKAGE_VERSION_DATA "beta")
elseif (BUILD_TYPE_NAME STREQUAL "")
@ -290,7 +289,7 @@ target_link_libraries(TeaSpeakServer
#Require a so
sqlite3
DataPipes::rtc::shared
DataPipes::core::shared
breakpad::static
protobuf::libprotobuf
@ -304,15 +303,10 @@ target_link_libraries(TeaSpeakServer
zstd::libzstd_static
)
if (COMPILE_WEB_CLIENT)
file(GLOB GLIB20_ARCHS ${glib20_DIR}/lib/*)
list(LENGTH GLIB20_ARCHS GLIB20_ARCHS_LENGTH)
if (NOT ${GLIB20_ARCHS_LENGTH} EQUAL 1)
message(FATAL_ERROR "Missing arch specific folder for glib2.0 in ${glib20_DIR}. Found ${GLIB20_ARCHS_LENGTH} directories, expected 1.")
endif ()
list(GET GLIB20_ARCHS 0 GLIB20_ARCH_DIR)
target_link_libraries(TeaSpeakServer ${GLIB20_ARCH_DIR}/libffi.so.7 ${nice_DIR}/lib/libnice.so.10)
endif ()
target_link_libraries(TeaSpeakServer
${CMAKE_SOURCE_DIR}/rtclib/target/debug/libteaspeak_rtc.so
${glib20_DIR}/lib/x86_64-linux-gnu/libffi.so.7 # FIXME: Remove this!
)
# include_directories(${LIBRARY_PATH}/boringssl/include/)
target_link_libraries(TeaSpeakServer

View File

@ -44,9 +44,10 @@ extern void testTomMath();
#define DB_NAME "TeaData.sqlite"
#endif
#include <codecvt>
#include <src/terminal/PipedTerminal.h>
#include "src/client/music/internal_provider/channel_replay/ChannelProvider.h"
#include <codecvt>
#include <src/rtc/lib.h>
#include <src/terminal/PipedTerminal.h>
class CLIParser {
public:
@ -189,7 +190,6 @@ int main(int argc, char** argv) {
return 0;
}
#endif
CLIParser arguments(argc, argv);
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
@ -311,9 +311,12 @@ int main(int argc, char** argv) {
//http://git.mcgalaxy.de/WolverinDEV/tomcrypt/blob/develop/src/misc/crypt/crypt_inits.c#L40-86
std::string descriptors = "LTGE";
bool crypt_init = false;
for(const auto& c : descriptors)
if((crypt_init |= crypt_mp_init(&c)))
for(const auto& c : descriptors) {
if((crypt_init |= crypt_mp_init(&c))) {
break;
}
}
if(!crypt_init) {
logCritical(LOG_GENERAL, "Could not initialise libtomcrypt mp descriptors!");
return 1;
@ -363,6 +366,18 @@ int main(int argc, char** argv) {
};
logger::updateLogLevels();
logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
{
debugMessage(LOG_GENERAL, "Initializing RTP library version {}", ts::rtc::version());
std::string error;
if(!ts::rtc::initialize(error)) {
logCritical(LOG_GENERAL, "Failed to initialize RTC library: {}", error);
return EXIT_FAILURE;
}
}
if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){
logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string());
logMessageFmt(true, LOG_GENERAL, strobf(" §aThank you for buying the TeaSpeak-§lPremium-§aSoftware! ").string());
@ -409,7 +424,6 @@ int main(int argc, char** argv) {
rlimit_updates:;
}
logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
logMessage(LOG_GENERAL, "Starting music providers");
if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]");

View File

@ -64,13 +64,14 @@ bool config::server::authentication::name;
bool config::server::clients::teamspeak;
std::string config::server::clients::extra_welcome_message_teamspeak;
std::string config::server::clients::teamspeak_not_allowed_message;
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teamspeak;
bool config::server::clients::teaweb;
std::string config::server::clients::extra_welcome_message_teaweb;
std::string config::server::clients::teaweb_not_allowed_message;
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teaweb;
bool config::server::clients::teaspeak;
std::string config::server::clients::extra_welcome_message_teaspeak;
config::server::clients::WelcomeMessageType config::server::clients::extra_welcome_message_type_teaspeak;
@ -1365,6 +1366,12 @@ std::deque<std::shared_ptr<EntryBinding>> config::create_bindings() {
ADD_DESCRIPTION("Allow/disallow the TeamSpeak 3 client to join the server.");
ADD_NOTE_RELOADABLE();
}
{
CREATE_BINDING("not_allowed_message", FLAG_RELOADABLE);
BIND_STRING(config::server::clients::teamspeak_not_allowed_message, "");
ADD_DESCRIPTION("Chaneg the message, displayed when denying the access to the server");
ADD_NOTE_RELOADABLE();
}
{
CREATE_BINDING("teamspeak_message", FLAG_RELOADABLE);
BIND_STRING(config::server::clients::extra_welcome_message_teamspeak, "");
@ -1390,7 +1397,6 @@ std::deque<std::shared_ptr<EntryBinding>> config::create_bindings() {
ADD_NOTE_RELOADABLE();
}
*/
config::server::clients::teaspeak = true;
{
CREATE_BINDING("teaspeak_message", FLAG_RELOADABLE);
BIND_STRING(config::server::clients::extra_welcome_message_teaspeak, "");
@ -1415,6 +1421,12 @@ std::deque<std::shared_ptr<EntryBinding>> config::create_bindings() {
ADD_DESCRIPTION("Allow/disallow the TeaSpeak - Web client to join the server.");
ADD_NOTE_RELOADABLE();
}
{
CREATE_BINDING("not_allowed_message", FLAG_RELOADABLE);
BIND_STRING(config::server::clients::teaweb_not_allowed_message, "");
ADD_DESCRIPTION("Chaneg the message, displayed when denying the access to the server");
ADD_NOTE_RELOADABLE();
}
{
CREATE_BINDING("teaweb_message", FLAG_RELOADABLE);
BIND_STRING(config::server::clients::extra_welcome_message_teaweb, "");

View File

@ -103,14 +103,15 @@ namespace ts::config {
};
extern bool teamspeak;
extern std::string teamspeak_not_allowed_message;
extern std::string extra_welcome_message_teamspeak;
extern WelcomeMessageType extra_welcome_message_type_teamspeak;
extern bool teaspeak;
extern std::string extra_welcome_message_teaspeak;
extern WelcomeMessageType extra_welcome_message_type_teaspeak;
extern bool teaweb;
extern std::string teaweb_not_allowed_message;
extern std::string extra_welcome_message_teaweb;
extern WelcomeMessageType extra_welcome_message_type_teaweb;

View File

@ -248,13 +248,6 @@ bool VirtualServer::could_default_create_channel() {
return false;
}
/*
*
for (auto &cl : this->server->getClients())
if (cl->isClientVisible(client) || client == cl)
cl->notifyClientLeftViewKicked(client, client->currentChannel, nullptr, cmd["reasonmsg"].as<std::string>(), this);
*/
void VirtualServer::notify_client_ban(const shared_ptr<ConnectedClient> &target, const std::shared_ptr<ts::server::ConnectedClient> &invoker, const std::string &reason, size_t time) {
/* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */
lock_guard command_lock(target->command_lock);
@ -307,6 +300,9 @@ void VirtualServer::notify_client_kick(
auto s_channel = dynamic_pointer_cast<ServerChannel>(target->currentChannel);
s_channel->unregister_client(target);
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
this->rtc_server().assign_channel(client->rtc_client_id, 0);
}
}
/* now disconnect the target itself */
@ -473,9 +469,17 @@ void VirtualServer::client_move(
}
});
if(s_source_channel)
if(s_source_channel) {
s_source_channel->unregister_client(target);
}
s_target_channel->register_client(target);
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
this->rtc_server().assign_channel(client->rtc_client_id, s_target_channel->rtc_channel_id);
}
if(auto client{dynamic_pointer_cast<VoiceClient>(target)}; client) {
/* Start normal broadcasting, what the client expects */
this->rtc_server().start_broadcast(client->rtc_client_id, 1, 1);
}
} else {
/* client left the server */
if(target->currentChannel) {
@ -489,6 +493,9 @@ void VirtualServer::client_move(
}
s_source_channel->unregister_client(target);
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
this->rtc_server().assign_channel(client->rtc_client_id, 0);
}
}
}
TIMING_STEP(timings, "notify view");

View File

@ -208,7 +208,7 @@ void VirtualServer::executeServerTick() {
{
BEGIN_TIMINGS();
this->serverStatistics->tick();
this->server_statistics_->tick();
{
lock_guard<threads::Mutex> lock(this->join_attempts_lock);
if(system_clock::now() > this->join_last_decrease + seconds(5)) {
@ -248,7 +248,7 @@ void VirtualServer::executeServerTick() {
BEGIN_TIMINGS();
if(this->conversation_cache_cleanup_timestamp + minutes(15) < system_clock::now()) {
debugMessage(this->serverId, "Cleaning up conversation cache.");
this->_conversation_manager->cleanup_cache();
this->conversation_manager_->cleanup_cache();
conversation_cache_cleanup_timestamp = system_clock::now();
}
END_TIMINGS(timing_ccache);
@ -256,7 +256,7 @@ void VirtualServer::executeServerTick() {
{
BEGIN_TIMINGS();
this->musicManager->execute_tick();
this->music_manager_->execute_tick();
END_TIMINGS(music_manager);
}

View File

@ -24,6 +24,7 @@
#include "InstanceHandler.h"
#include "Configuration.h"
#include "VirtualServer.h"
#include "./rtc/lib.h"
#include "src/manager/ConversationManager.h"
#include <misc/sassert.h>
#include <src/manager/ActionLogger.h>
@ -48,6 +49,8 @@ VirtualServer::VirtualServer(uint16_t serverId, sql::SqlManager* database) : ser
bool VirtualServer::initialize(bool test_properties) {
assert(self.lock());
this->rtc_server_ = std::make_unique<rtc::Server>();
this->_properties = serverInstance->databaseHelper()->loadServerProperties(self.lock());
this->_properties->registerNotifyHandler([&](Property& prop){
if(prop.type() == property::VIRTUALSERVER_DISABLE_IP_SAVING) {
@ -179,8 +182,8 @@ bool VirtualServer::initialize(bool test_properties) {
properties()[property::VIRTUALSERVER_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(base64::encode(buffer, bufferLength)));
}
this->_conversation_manager = make_shared<conversation::ConversationManager>(this->ref());
this->_conversation_manager->initialize(this->_conversation_manager);
this->conversation_manager_ = make_shared<conversation::ConversationManager>(this->ref());
this->conversation_manager_->initialize(this->conversation_manager_);
channelTree = new ServerChannelTree(self.lock(), this->sql);
channelTree->loadChannelsFromDatabase();
@ -243,7 +246,7 @@ bool VirtualServer::initialize(bool test_properties) {
letters = new letter::LetterManager(this);
serverStatistics = make_shared<stats::ConnectionStatistics>(serverInstance->getStatistics());
server_statistics_ = make_shared<stats::ConnectionStatistics>(serverInstance->getStatistics());
this->serverRoot = std::make_shared<InternalClient>(this->sql, self.lock(), this->properties()[property::VIRTUALSERVER_NAME].as<string>(), false);
static_pointer_cast<InternalClient>(this->serverRoot)->setSharedLock(this->serverRoot);
@ -286,10 +289,10 @@ bool VirtualServer::initialize(bool test_properties) {
}
this->channelTree->printChannelTree([&](std::string msg){ debugMessage(this->serverId, msg); });
this->musicManager = make_shared<music::MusicBotManager>(self.lock());
this->musicManager->_self = this->musicManager;
this->musicManager->load_playlists();
this->musicManager->load_bots();
this->music_manager_ = make_shared<music::MusicBotManager>(self.lock());
this->music_manager_->_self = this->music_manager_;
this->music_manager_->load_playlists();
this->music_manager_->load_bots();
#if 0
if(this->properties()[property::VIRTUALSERVER_ICON_ID] != (IconId) 0)
@ -316,7 +319,7 @@ bool VirtualServer::initialize(bool test_properties) {
}
/* lets cleanup the conversations for not existent channels */
this->_conversation_manager->synchronize_channels();
this->conversation_manager_->synchronize_channels();
return true;
}
@ -327,7 +330,7 @@ VirtualServer::~VirtualServer() {
delete this->channelTree;
delete this->letters;
delete this->complains;
this->_conversation_manager.reset();
this->conversation_manager_.reset();
if(this->_serverKey) ecc_free(this->_serverKey);
delete this->_serverKey;
@ -511,8 +514,8 @@ bool VirtualServer::start(std::string& error) {
properties()[property::VIRTUALSERVER_UPTIME] = 0;
this->startTimestamp = system_clock::now();
this->musicManager->cleanup_semi_bots();
this->musicManager->connectBots();
this->music_manager_->cleanup_semi_bots();
this->music_manager_->connectBots();
{
threads::MutexLock lock(this->stateLock);
@ -585,7 +588,7 @@ void VirtualServer::stop(const std::string& reason, bool disconnect_query) {
logError(this->serverId, "Got client with unknown type: " + to_string(cl->getType()));
}
}
this->musicManager->disconnectBots();
this->music_manager_->disconnectBots();
serverInstance->cancelExecute(this);

View File

@ -48,6 +48,10 @@ namespace ts {
class MusicBotManager;
}
namespace rtc {
class Server;
}
namespace server {
class ConnectedClient;
class VoiceClient;
@ -184,6 +188,7 @@ namespace ts {
inline ServerId getServerId(){ return this->serverId; }
inline ServerChannelTree* getChannelTree(){ return this->channelTree; }
inline GroupManager* getGroupManager() { return this->groups; }
inline rtc::Server& rtc_server() { return *this->rtc_server_; }
[[nodiscard]] inline auto getTokenManager() -> token::TokenManager* {
return this->tokenManager;
@ -210,7 +215,7 @@ namespace ts {
std::string getDisplayName(){ return properties()[property::VIRTUALSERVER_NAME]; }
std::shared_ptr<stats::ConnectionStatistics> getServerStatistics(){ return serverStatistics; }
std::shared_ptr<stats::ConnectionStatistics> getServerStatistics(){ return server_statistics_; }
std::shared_ptr<VoiceServer> getVoiceServer(){ return this->udpVoiceServer; }
WebControlServer* getWebServer(){ return this->webControlServer; }
@ -281,7 +286,7 @@ namespace ts {
void send_text_message(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* sender */, const std::string& /* message */);
inline int voice_encryption_mode() { return this->_voice_encryption_mode; }
inline std::shared_ptr<conversation::ConversationManager> conversation_manager() { return this->_conversation_manager; }
inline std::shared_ptr<conversation::ConversationManager> conversation_manager() { return this->conversation_manager_; }
inline auto& get_channel_tree_lock() { return this->channel_tree_lock; }
@ -305,9 +310,10 @@ namespace ts {
token::TokenManager* tokenManager = nullptr;
ComplainManager* complains = nullptr;
letter::LetterManager* letters = nullptr;
std::shared_ptr<music::MusicBotManager> musicManager;
std::shared_ptr<stats::ConnectionStatistics> serverStatistics;
std::shared_ptr<conversation::ConversationManager> _conversation_manager;
std::shared_ptr<music::MusicBotManager> music_manager_;
std::shared_ptr<stats::ConnectionStatistics> server_statistics_;
std::shared_ptr<conversation::ConversationManager> conversation_manager_;
std::unique_ptr<rtc::Server> rtc_server_;
sql::SqlManager* sql;

View File

@ -361,7 +361,7 @@ shared_ptr<VirtualServer> VirtualServerManager::create_server(std::string hosts,
server->properties()[property::VIRTUALSERVER_HOST] = hosts;
server->properties()[property::VIRTUALSERVER_PORT] = port;
if(config::server::default_music_bot) {
auto bot = server->musicManager->createBot(0);
auto bot = server->music_manager_->createBot(0);
if(!bot) {
logCritical(server->getServerId(), "Failed to create default music bot!");
}

View File

@ -12,9 +12,8 @@ using namespace std;
using namespace ts;
using namespace ts::server;
extern InstanceHandler* serverInstance;
ServerChannel::ServerChannel(ChannelId parentId, ChannelId channelId) : BasicChannel(parentId, channelId) {
ServerChannel::ServerChannel(uint32_t rtc_channel_id, ChannelId parentId, ChannelId channelId) : BasicChannel(parentId, channelId),
rtc_channel_id{rtc_channel_id} {
memtrack::allocated<ServerChannel>(this);
}
@ -59,18 +58,19 @@ void ServerChannel::setProperties(const std::shared_ptr<Properties> &ptr) {
BasicChannel::setProperties(ptr);
}
ServerChannelTree::ServerChannelTree(const std::shared_ptr<server::VirtualServer>& server, sql::SqlManager* sql) : sql(sql), server(server) { }
ServerChannelTree::ServerChannelTree(const std::shared_ptr<server::VirtualServer>& server, sql::SqlManager* sql) : sql(sql), server_ref(server) { }
ServerChannelTree::~ServerChannelTree() { }
void ServerChannelTree::deleteSemiPermanentChannels() {
loop:
for(const auto& ch : this->channels())
for(const auto& ch : this->channels()) {
if(ch->channelType() == ChannelType::semipermanent || ch->channelType() == ChannelType::temporary){ //We also delete private channels
this->delete_channel_root(ch);
goto loop;
}
}
}
ChannelId ServerChannelTree::generateChannelId() {
@ -90,12 +90,15 @@ std::shared_ptr<BasicChannel> ServerChannelTree::createChannel(ChannelId parentI
std::shared_ptr<BasicChannel> channel = BasicChannelTree::createChannel(parentId, orderId, name);
if(!channel) return channel;
auto properties = serverInstance->databaseHelper()->loadChannelProperties(this->server.lock(), channel->channelId());
for(const auto& prop : channel->properties().list_properties())
if(prop.isModified()) //Copy the already set properties
auto properties = serverInstance->databaseHelper()->loadChannelProperties(this->server_ref.lock(), channel->channelId());
for(const auto& prop : channel->properties().list_properties()) {
if(prop.isModified()) { //Copy the already set properties
(*properties)[prop.type()] = prop.value();
}
}
static_pointer_cast<ServerChannel>(channel)->setProperties(properties);
static_pointer_cast<ServerChannel>(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server.lock(), channel->channelId()));
static_pointer_cast<ServerChannel>(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server_ref.lock(), channel->channelId()));
channel->properties()[property::CHANNEL_CREATED_AT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
@ -121,7 +124,7 @@ inline std::shared_ptr<TreeView::LinkedTreeEntry> findLinkedChannelByPool(const
}
ServerId ServerChannelTree::getServerId() {
auto s = this->server.lock();
auto s = this->server_ref.lock();
return s ? s->getServerId() : 0UL;
}
@ -532,8 +535,16 @@ int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column)
if(channelId == 0)
return 0;
auto channel = std::make_shared<ServerChannel>(parentId, channelId);
auto server = this->server.lock();
auto server = this->server_ref.lock();
std::shared_ptr<ServerChannel> channel;
if(server) {
auto rtc_channel_id = server->rtc_server().create_channel();
channel = std::make_shared<ServerChannel>(rtc_channel_id, parentId, channelId);
} else {
channel = std::make_shared<ServerChannel>(0, parentId, channelId);
}
static_pointer_cast<ServerChannel>(channel)->setProperties(serverInstance->databaseHelper()->loadChannelProperties(server, channelId));
static_pointer_cast<ServerChannel>(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(server, channel->channelId()));
@ -544,24 +555,30 @@ int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column)
}
deque<ChannelId> ServerChannelTree::deleteChannelRoot(const std::shared_ptr<BasicChannel> &channel) {
auto server = this->server.lock();
auto server = this->server_ref.lock();
auto channels = this->delete_channel_root(channel);
deque<ChannelId> channel_ids;
for(const auto& channel : channels)
for(const auto& channel : channels) {
channel_ids.push_back(channel->channelId());
}
return channel_ids;
}
void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel> &channel) {
BasicChannelTree::on_channel_entry_deleted(channel);
auto server = this->server.lock();
auto server_channel = dynamic_pointer_cast<ServerChannel>(channel);
assert(server_channel);
auto server = this->server_ref.lock();
if(server) {
server->getGroupManager()->handleChannelDeleted(channel->channelId());
server->conversation_manager()->delete_conversation(channel->channelId());
} else
server->rtc_server().destroy_channel(server_channel->rtc_channel_id);
} else {
serverInstance->getGroupManager()->handleChannelDeleted(channel->channelId());
}
auto sql_result = sql::command(this->sql, "DELETE FROM `channels` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
@ -570,11 +587,18 @@ void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel>
sql_result = sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `id` = '" + to_string(channel->channelId()) + "' AND `type` = " + to_string(property::PropertyType::PROP_TYPE_CHANNEL)).execute();
LOG_SQL_CMD(sql_result);
serverInstance->databaseHelper()->deleteChannelPermissions(this->server.lock(), channel->channelId());
serverInstance->databaseHelper()->deleteChannelPermissions(this->server_ref.lock(), channel->channelId());
sql_result = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
LOG_SQL_CMD(sql_result);
}
std::shared_ptr<BasicChannel> ServerChannelTree::allocateChannel(const shared_ptr<BasicChannel> &parent, ChannelId channelId) {
return std::make_shared<ServerChannel>(parent ? parent->channelId() : 0, channelId);
auto server = this->server_ref.lock();
if(server) {
auto rtc_channel_id = server->rtc_server().create_channel();
return std::make_shared<ServerChannel>(rtc_channel_id, parent->channelId(), channelId);
} else {
return std::make_shared<ServerChannel>(0, parent->channelId(), channelId);
}
}

View File

@ -1,11 +1,11 @@
#pragma once
#include <stdint.h>
#include <cstdlib>
#include "Properties.h"
#include "PermissionManager.h"
#include "BasicChannel.h"
#include "../Group.h"
#include "../rtc/lib.h"
#include <memory>
#include <sql/SqlQuery.h>
@ -19,11 +19,13 @@ namespace ts {
class ServerChannel : public BasicChannel {
friend class ServerChannelTree;
public:
ServerChannel(ChannelId parentId, ChannelId channelId);
ServerChannel(uint32_t rtc_channel_id, ChannelId parentId, ChannelId channelId);
~ServerChannel() override;
~ServerChannel();
void setProperties(const std::shared_ptr<Properties> &ptr) override;
uint32_t rtc_channel_id;
std::shared_mutex client_lock;
std::deque<std::weak_ptr<server::ConnectedClient>> clients;
@ -37,24 +39,24 @@ namespace ts {
class ServerChannelTree : public BasicChannelTree {
public:
ServerChannelTree(const std::shared_ptr<server::VirtualServer>&, sql::SqlManager*);
virtual ~ServerChannelTree();
~ServerChannelTree() override;
void loadChannelsFromDatabase();
virtual std::shared_ptr<BasicChannel> createChannel(ChannelId parentId, ChannelId orderId, const std::string &name) override;
std::shared_ptr<BasicChannel> createChannel(ChannelId parentId, ChannelId orderId, const std::string &name) override;
virtual std::deque<ChannelId> deleteChannelRoot(const std::shared_ptr<BasicChannel> &channel);
void deleteSemiPermanentChannels();
std::shared_ptr<LinkedTreeEntry> tree_head() { return this->head; }
protected:
virtual ChannelId generateChannelId() override;
ChannelId generateChannelId() override;
virtual void on_channel_entry_deleted(const std::shared_ptr<BasicChannel> &channel) override;
void on_channel_entry_deleted(const std::shared_ptr<BasicChannel> &channel) override;
std::shared_ptr<BasicChannel> allocateChannel(const std::shared_ptr<BasicChannel> &parent, ChannelId channelId) override;
private:
std::weak_ptr<server::VirtualServer> server;
std::weak_ptr<server::VirtualServer> server_ref;
ServerId getServerId();
sql::SqlManager* sql;

View File

@ -127,10 +127,10 @@ namespace ts {
/** Notifies (after request) */
bool sendNeededPermissions(bool /* force an update */); /* invoke this because it dosn't spam the client */
virtual bool notifyClientNeededPermissions();
virtual bool notifyServerGroupList();
virtual bool notifyServerGroupList(bool as_notify = true);
virtual bool notifyGroupPermList(const std::shared_ptr<Group>&, bool);
virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr<permission::v2::PermissionManager>&, bool);
virtual bool notifyChannelGroupList();
virtual bool notifyChannelGroupList(bool as_notify = true);
virtual bool notifyConnectionInfo(const std::shared_ptr<ConnectedClient> &target, const std::shared_ptr<ConnectionInfoData> &info);
virtual bool notifyChannelSubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);
virtual bool notifyChannelUnsubscribed(const std::deque<std::shared_ptr<BasicChannel>> &);

View File

@ -46,8 +46,8 @@ do { \
} \
} while(0)
bool ConnectedClient::notifyServerGroupList() {
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyservergrouplist" : "");
bool ConnectedClient::notifyServerGroupList(bool as_notify) {
Command cmd(as_notify ? "notifyservergrouplist" : "");
int index = 0;
for (const auto& group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableServerGroups(true)) {
@ -173,8 +173,8 @@ bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_
return true;
}
bool ConnectedClient::notifyChannelGroupList() {
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : "");
bool ConnectedClient::notifyChannelGroupList(bool as_notify) {
Command cmd(as_notify ? "notifychannelgrouplist" : "");
int index = 0;
for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) {
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) {

View File

@ -196,7 +196,7 @@ bool ConnectedClient::handle_text_command(
return true;
}
auto botId = static_cast<ClientDbId>(stoll(arguments[1]));
auto bot = this->server->musicManager->findBotById(botId);
auto bot = this->server->music_manager_->findBotById(botId);
if (!bot) ERR(serverInstance->musicRoot(), "Could not find target bot");
if(bot->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId() &&
!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_music_channel_list, this->getChannelId())) &&
@ -230,7 +230,7 @@ bool ConnectedClient::handle_text_command(
} else if (TARG(0, "delete")) {
GBOT(bot, true);
PERM_CHECK_BOT(i_client_music_delete_power, i_client_music_needed_delete_power, "You don't have the permission to rename this music bot");
this->server->musicManager->deleteBot(bot);
this->server->music_manager_->deleteBot(bot);
send_message(bot, "You successfully deleted this music bot!");
return true;
} else if(TARG(0, "yt") || TARG(0, "soundcloud") || TARG(0, "sc")){

View File

@ -26,6 +26,17 @@ 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};
SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient(a, b) {
speak_begin = std::chrono::system_clock::now();
speak_last_packet = std::chrono::system_clock::now();
};
SpeakingClient::~SpeakingClient() {
if(auto server{this->server}; this->rtc_client_id > 0 && server) {
server->rtc_server().destroy_client(this->rtc_client_id);
}
}
bool SpeakingClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
//if(this->properties()[property::CLIENT_AWAY].as<bool>()) return false;
if(!this->properties()[property::CLIENT_OUTPUT_HARDWARE].as<bool>()) return false;
@ -47,76 +58,14 @@ bool SpeakingClient::shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedCl
return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power, false);
}
void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head, bool fragmented) {
auto server = this->getServer();
auto self = _this.lock();
if(!self || !server) return;
if(data.length() < 3) {
this->disconnect("invalid packet (Voice; Length: " + to_string(data.length()) + ")");
return;
}
#if 0
if(rand() % 10 == 0) {
logMessage(0, "Dropping audio packet");
return;
}
logMessage(0, "Received voice: Head: {} Fragmented: {}, length: {}", head, fragmented, data.length());
#endif
bool SpeakingClient::should_handle_voice_packet(size_t) {
auto current_channel = this->currentChannel;
if(!current_channel) { return; }
if(!this->allowedToTalk) { return; }
if(!current_channel) { return false; }
if(!this->allowedToTalk) { return false; }
this->updateSpeak(false, system_clock::now());
this->resetIdleTime();
auto target_clients = this->server->getClientsByChannel(current_channel);
target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr<ConnectedClient>& client) {
if(client == this) return true;
auto speaking_client = dynamic_pointer_cast<SpeakingClient>(client);
if(!speaking_client) return true;
return !speaking_client->shouldReceiveVoice(self);
}), target_clients.end());
if(target_clients.empty()) {
return;
}
VoicePacketFlags flags{};
flags.head = head;
flags.fragmented = fragmented;
flags.new_protocol = false;
{
//crypt_mode = 1 | disabled
//crypt_mode = 2 | enabled
auto crypt_mode = this->server->voice_encryption_mode();
if(crypt_mode == 0)
flags.encrypted = !current_channel->properties()[property::CHANNEL_CODEC_IS_UNENCRYPTED].as<bool>();
else
flags.encrypted = crypt_mode == 2;
}
uint16_t vpacketId = be2le16((char*) data.data_ptr());
auto codec = (uint8_t) data[2];
#ifdef PKT_LOG_VOICE
logTrace(lstream << CLIENT_LOG_PREFIX << "Voice length: " << data.length() << " -> id: " << vpacketId << " codec: " << (int) codec << " head: " << head << " fragmented: " << fragmented);
#endif
char buffer[data.length() + 2];
le2be16(vpacketId, &buffer[0]);
le2be16(getClientId(), &buffer[2]);
buffer[4] = codec;
if(data.length() - 3 > 0) {
memcpy(&buffer[5], &data[3], data.length() - 3);
}
auto bview = pipes::buffer_view{buffer, data.length() + 2};
for (const auto& client : target_clients) {
auto speaking_client = static_pointer_cast<SpeakingClient>(client);
speaking_client->send_voice_packet(bview, flags);
}
return true;
}
//2 + 2 + 8
@ -755,6 +704,19 @@ void SpeakingClient::processJoin() {
TIMING_STEP(timings, "setup ");
ref_server->registerClient(_this.lock());
if(this->rtc_client_id) {
/* in case of client reconnect */
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()));
} else if(this->getType() == ClientType::CLIENT_TEASPEAK) {
/* 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()));
} else if(this->getType() == ClientType::CLIENT_WEB) {
this->rtc_client_id = this->server->rtc_server().create_rtp_client(dynamic_pointer_cast<SpeakingClient>(this->ref()));
}
TIMING_STEP(timings, "server reg ");
ref_server->getGroupManager()->cleanupAssignments(this->getClientDatabaseId());
TIMING_STEP(timings, "grp cleanup");
@ -868,7 +830,7 @@ void SpeakingClient::processLeave() {
server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */
}
server->groups->disableCache(ownLock->getClientDatabaseId());
server->musicManager->cleanup_client_bots(this->getClientDatabaseId());
server->music_manager_->cleanup_client_bots(this->getClientDatabaseId());
//ref_server = nullptr; Removed caused nullptr exceptions
}
{ //Delete own viewing clients
@ -885,23 +847,29 @@ void SpeakingClient::triggerVoiceEnd() {
this->properties()[property::CLIENT_FLAG_TALKING] = false;
}
void SpeakingClient::updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &now) {
threads::MutexLock lock(this->speak_lock);
void SpeakingClient::updateSpeak(bool only_update, const std::chrono::system_clock::time_point &now) {
std::lock_guard speak_lock{this->speak_mutex};
if(this->speak_last_packet + this->speak_accuracy < now) {
if(this->speak_last_packet > this->speak_begin) {
if(!this->properties()[property::CLIENT_FLAG_TALKING].as<bool>())
if(!this->properties()[property::CLIENT_FLAG_TALKING].as<bool>()) {
this->properties()[property::CLIENT_FLAG_TALKING] = true;
}
this->speak_time += duration_cast<milliseconds>(this->speak_last_packet - this->speak_begin);
} else {
if(this->properties()[property::CLIENT_FLAG_TALKING].as<bool>())
if(this->properties()[property::CLIENT_FLAG_TALKING].as<bool>()) {
this->properties()[property::CLIENT_FLAG_TALKING] = false;
}
}
this->speak_begin = now;
this->speak_last_packet = now;
}
if(!onlyUpdate)
if(!only_update) {
this->speak_last_packet = now;
}
}
void SpeakingClient::tick(const std::chrono::system_clock::time_point &time) {
@ -930,19 +898,128 @@ command_result SpeakingClient::handleCommand(Command &command) {
if(this->connectionState() == ConnectionState::INIT_HIGH) {
if(this->handshake.state == HandshakeState::BEGIN || this->handshake.state == HandshakeState::IDENTITY_PROOF) {
command_result result;
if(command.command() == "handshakebegin")
if(command.command() == "handshakebegin") {
result.reset(this->handleCommandHandshakeBegin(command));
else if(command.command() == "handshakeindentityproof")
} else if(command.command() == "handshakeindentityproof") {
result.reset(this->handleCommandHandshakeIdentityProof(command));
else
} else {
result.reset(command_result{error::client_not_logged_in});
}
if(result.has_error())
if(result.has_error()) {
this->postCommandHandler.push_back([&]{
this->close_connection(system_clock::now() + seconds(1));
});
}
return result;
}
} else if(this->connectionState() == ConnectionState::CONNECTED) {
if(command.command() == "rtcsessiondescribe") {
return this->handleCommandRtcSessionDescribe(command);
} else if(command.command() == "rtcicecandidate") {
return this->handleCommandRtcIceCandidate(command);
} else if(command.command() == "rtcbroadcast") {
return this->handleCommandRtcBroadcast(command);
} else if(command.command() == "rtcsessionreset") {
return this->handleCommandRtcSessionReset(command);
}
}
return ConnectedClient::handleCommand(command);
}
command_result SpeakingClient::handleCommandRtcSessionDescribe(Command &command) {
CMD_REQ_SERVER;
CMD_CHK_AND_INC_FLOOD_POINTS(15);
uint32_t mode;
if(command["mode"].string() == "offer") {
mode = 1;
} else if(command["mode"].string() == "answer") {
mode = 2;
} else {
return command_result{error::parameter_invalid, "mode"};
}
std::string error{};
if(!this->server->rtc_server().apply_remote_description(error, this->rtc_client_id, mode, command["sdp"])) {
return command_result{error::vs_critical, error};
}
if(mode == 1) {
std::string result{};
if(!this->server->rtc_server().generate_local_description(this->rtc_client_id, result)) {
return command_result{error::vs_critical, result};
} else {
ts::command_builder notify{"notifyrtcsessiondescription"};
notify.put_unchecked(0, "mode", "answer");
notify.put_unchecked(0, "sdp", result);
this->sendCommand(notify);
}
}
return command_result{error::ok};
}
command_result SpeakingClient::handleCommandRtcSessionReset(Command &command) {
CMD_REQ_SERVER;
CMD_CHK_AND_INC_FLOOD_POINTS(15);
this->server->rtc_server().reset_rtp_session(this->rtc_client_id);
return command_result{error::ok};
}
command_result SpeakingClient::handleCommandRtcIceCandidate(Command &command) {
CMD_REQ_SERVER;
std::string error;
if(command[0].has("candidate")) {
auto candidate = command["candidate"].string();
if(!this->server->rtc_server().add_ice_candidate(error, this->rtc_client_id, command["media_line"], candidate)) {
return command_result{error::vs_critical, error};
}
} else {
this->server->rtc_server().ice_candidates_finished(this->rtc_client_id);
}
return command_result{error::ok};
}
command_result SpeakingClient::handleCommandRtcBroadcast(Command &command) {
CMD_REQ_SERVER;
CMD_CHK_AND_INC_FLOOD_POINTS(15);
/* TODO: Filter out duplicates */
std::vector<std::tuple<uint8_t, uint32_t>> broadcasts{};
broadcasts.reserve(command.bulkCount());
for(size_t index{0}; index < command.bulkCount(); index++) {
auto& bulk = command[index];
broadcasts.push_back(std::make_tuple(bulk["type"], bulk.has("ssrc") ? bulk["ssrc"].as<uint32_t>() : (uint32_t) 0));
}
ts::command_result_bulk result{};
for(size_t index{0}; index < command.bulkCount(); index++) {
auto broadcast_result = this->server->rtc_server().start_broadcast(this->rtc_client_id, std::get<0>(broadcasts[index]), std::get<1>(broadcasts[index]));
switch(broadcast_result) {
case rtc::BroadcastStartResult::Success:
result.emplace_result(error::ok);
break;
case rtc::BroadcastStartResult::InvalidBroadcastType:
result.emplace_result(error::parameter_invalid, "type");
break;
case rtc::BroadcastStartResult::InvalidStreamId:
result.emplace_result(error::rtc_missing_target_channel);
break;
case rtc::BroadcastStartResult::ClientHasNoChannel:
result.emplace_result(error::vs_critical, "no channel");
break;
case rtc::BroadcastStartResult::InvalidClient:
result.emplace_result(error::vs_critical, "invalid client");
break;
case rtc::BroadcastStartResult::UnknownError:
default:
result.emplace_result(error::vs_critical, "unknown error");
}
}
return ts::command_result{std::move(result)};
}

View File

@ -1,7 +1,8 @@
#pragma once
#include <json/json.h>
#include "ConnectedClient.h"
#include <json/json.h>
#include <src/rtc/lib.h>
namespace ts::server {
class VirtualServer;
@ -31,11 +32,8 @@ namespace ts::server {
UNSET = 0xff
};
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) {
speak_begin = std::chrono::system_clock::now();
speak_last_packet = std::chrono::system_clock::now();
};
~SpeakingClient() override = default;
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b);
~SpeakingClient() override;
//Voice
virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
@ -53,7 +51,6 @@ namespace ts::server {
protected:
void tick(const std::chrono::system_clock::time_point &time) override;
protected:
public:
void updateChannelClientProperties(bool channel_lock, bool notify) override;
@ -61,22 +58,27 @@ namespace ts::server {
command_result handleCommand(Command &command) override;
public:
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
bool should_handle_voice_packet(size_t /* size */);
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */, bool /* head */);
void processJoin();
virtual void processJoin();
void processLeave();
virtual command_result handleCommandHandshakeBegin(Command&);
virtual command_result handleCommandHandshakeIdentityProof(Command &);
virtual command_result handleCommandClientInit(Command&);
virtual command_result handleCommandRtcSessionDescribe(Command &command);
virtual command_result handleCommandRtcSessionReset(Command &command);
virtual command_result handleCommandRtcIceCandidate(Command &);
virtual command_result handleCommandRtcBroadcast(Command &);
void triggerVoiceEnd();
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1};
std::chrono::milliseconds speak_accuracy{1000};
threads::Mutex speak_lock;
std::chrono::milliseconds speak_time = std::chrono::milliseconds{0};
std::mutex speak_mutex;
std::chrono::milliseconds speak_time{0};
std::chrono::system_clock::time_point speak_begin;
std::chrono::system_clock::time_point speak_last_packet;
@ -94,5 +96,7 @@ namespace ts::server {
//TeaSpeak
std::shared_ptr<Json::Value> identityData;
} handshake;
rtc::RTCClientId rtc_client_id{0};
};
}

View File

@ -421,7 +421,7 @@ command_result ConnectedClient::handleCommandChannelGroupList(Command &) {
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_list, 1);
this->notifyChannelGroupList();
this->notifyChannelGroupList(this->getType() != ClientType::CLIENT_QUERY);
this->command_times.servergrouplist = system_clock::now();
return command_result{error::ok};
}
@ -1036,6 +1036,10 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) {
}
} else if (key == "channel_codec") {
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec, 1, channel_id);
auto value = cmd[key].as<uint32_t>();
if(!(value >= 4 && value <= 5)) {
return command_result{error::parameter_invalid, "channel_codec"};
}
} else if (key == "channel_codec_quality") {
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec_quality, 1, channel_id);
} else if (key == "channel_codec_is_unencrypted") {

View File

@ -2857,6 +2857,7 @@ command_result ConnectedClient::handleCommandListFeatureSupport(ts::Command &cmd
REGISTER_FEATURE("advanced-channel-chat", FeatureSupportMode::FULL, 1);
REGISTER_FEATURE("log-query", FeatureSupportMode::FULL, 1);
REGISTER_FEATURE("whisper-echo", FeatureSupportMode::FULL, 1);
REGISTER_FEATURE("video", FeatureSupportMode::EXPERIMENTAL, 1);
this->sendCommand(notify);
return command_result{error::ok};

View File

@ -57,7 +57,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
if(this->server->musicManager->max_bots() != -1 && this->server->musicManager->max_bots() <= this->server->musicManager->current_bot_count()){
if(this->server->music_manager_->max_bots() != -1 && this->server->music_manager_->max_bots() <= this->server->music_manager_->current_bot_count()){
if(config::license->isPremium())
return command_result{error::music_limit_reached};
else
@ -79,7 +79,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) {
auto max_bots = permissions[permission::i_client_music_limit];
if(max_bots.has_value) {
auto ownBots = this->server->musicManager->listBots(this->getClientDatabaseId());
auto ownBots = this->server->music_manager_->listBots(this->getClientDatabaseId());
if(!permission::v2::permission_granted(ownBots.size() + 1, max_bots))
return command_result{error::music_client_limit_reached};
}
@ -125,7 +125,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) {
if(!channel)
channel = this->server->channelTree->getDefaultChannel();
auto bot = this->server->musicManager->createBot(this->getClientDatabaseId());
auto bot = this->server->music_manager_->createBot(this->getClientDatabaseId());
if(!bot) return command_result{error::vs_critical};
bot->set_bot_type(create_type);
if(permissions[permission::i_client_music_create_modify_max_volume].has_value) {
@ -169,21 +169,21 @@ command_result ConnectedClient::handleCommandMusicBotDelete(Command& cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
if(bot->getOwner() != this->getClientDatabaseId()) {
ACTION_REQUIRES_PERMISSION(permission::i_client_music_delete_power, bot->calculate_permission(permission::i_client_music_needed_delete_power, bot->getChannelId()), this->getChannelId());
}
this->server->musicManager->deleteBot(bot);
this->server->music_manager_->deleteBot(bot);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandMusicBotSetSubscription(ts::Command &cmd) {
if(!config::music::enabled) return command_result{error::music_disabled};
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
if(!bot && cmd["bot_id"].as<ClientDbId>() != 0) return command_result{error::music_invalid_id};
{
@ -229,7 +229,7 @@ command_result ConnectedClient::handleCommandMusicBotPlayerInfo(Command& cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymusicplayerinfo" : "");
@ -267,7 +267,7 @@ command_result ConnectedClient::handleCommandMusicBotPlayerAction(Command& cmd)
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = this->server->musicManager->findBotById(cmd["bot_id"]);
auto bot = this->server->music_manager_->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
ACTION_REQUIRES_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, bot->getChannelId()), this->getChannelId());
@ -299,7 +299,7 @@ command_result ConnectedClient::handleCommandPlaylistList(ts::Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto self_ref = this->ref();
auto playlists = this->server->musicManager->playlists();
auto playlists = this->server->music_manager_->playlists();
playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr<music::PlayablePlaylist>& playlist) {
return playlist->client_has_permissions(self_ref, permission::i_playlist_needed_view_power, permission::i_playlist_view_power, music::PlaylistPermissions::do_no_require_granted) != permission::ok;
@ -354,13 +354,13 @@ command_result ConnectedClient::handleCommandPlaylistCreate(ts::Command &cmd) {
{
auto max_playlists = this->calculate_permission(permission::i_max_playlists, 0);
if(max_playlists.has_value) {
auto playlists = ref_server->musicManager->find_playlists_by_client(this->getClientDatabaseId());
auto playlists = ref_server->music_manager_->find_playlists_by_client(this->getClientDatabaseId());
if(!permission::v2::permission_granted(playlists.size(), max_playlists))
return command_result{permission::i_max_playlists};
}
}
auto playlist = ref_server->musicManager->create_playlist(this->getClientDatabaseId(), this->getDisplayName());
auto playlist = ref_server->music_manager_->create_playlist(this->getClientDatabaseId(), this->getDisplayName());
if(!playlist) return command_result{error::vs_critical};
playlist->properties()[property::PLAYLIST_TYPE] = music::Playlist::Type::GENERAL;
@ -399,14 +399,14 @@ command_result ConnectedClient::handleCommandPlaylistDelete(ts::Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_delete_power, permission::i_playlist_delete_power); perr)
return command_result{perr};
string error;
if(!ref_server->musicManager->delete_playlist(playlist->playlist_id(), error)) {
if(!ref_server->music_manager_->delete_playlist(playlist->playlist_id(), error)) {
logError(this->getServerId(), "Failed to delete playlist with id {}. Error: {}", playlist->playlist_id(), error);
return command_result{error::vs_critical};
}
@ -419,7 +419,7 @@ command_result ConnectedClient::handleCommandPlaylistInfo(ts::Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power, music::PlaylistPermissions::do_no_require_granted); perr)
@ -440,7 +440,7 @@ command_result ConnectedClient::handleCommandPlaylistEdit(ts::Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_modify_power, permission::i_playlist_modify_power); perr)
@ -498,7 +498,7 @@ command_result ConnectedClient::handleCommandPlaylistPermList(ts::Command &cmd)
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
{
@ -551,7 +551,7 @@ command_result ConnectedClient::handleCommandPlaylistAddPerm(ts::Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
@ -581,7 +581,7 @@ command_result ConnectedClient::handleCommandPlaylistDelPerm(ts::Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
@ -611,7 +611,7 @@ command_result ConnectedClient::handleCommandPlaylistClientList(ts::Command &cmd
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
{
@ -647,7 +647,7 @@ command_result ConnectedClient::handleCommandPlaylistClientPermList(ts::Command
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
{
@ -710,7 +710,7 @@ command_result ConnectedClient::handleCommandPlaylistClientAddPerm(ts::Command &
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
auto client_id = cmd[0]["cldbid"].as<ClientDbId>();
@ -743,7 +743,7 @@ command_result ConnectedClient::handleCommandPlaylistClientDelPerm(ts::Command &
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
auto client_id = cmd[0]["cldbid"].as<ClientDbId>();
@ -815,7 +815,7 @@ command_result ConnectedClient::handleCommandPlaylistSongList(ts::Command &cmd)
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power); perr)
@ -860,7 +860,7 @@ command_result ConnectedClient::handleCommandPlaylistSongSetCurrent(ts::Command
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_move_power, permission::i_playlist_song_move_power); perr)
@ -877,7 +877,7 @@ command_result ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_add_power, permission::i_playlist_song_add_power); perr)
@ -917,7 +917,7 @@ command_result ConnectedClient::handleCommandPlaylistSongReorder(ts::Command &cm
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_move_power, permission::i_playlist_song_move_power); perr)
@ -940,7 +940,7 @@ command_result ConnectedClient::handleCommandPlaylistSongRemove(ts::Command &cmd
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_remove_power, permission::i_playlist_song_remove_power); perr)
@ -1135,21 +1135,21 @@ command_result ConnectedClient::handleCommandMusicBotPlaylistAssign(ts::Command
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto bot = ref_server->musicManager->findBotById(cmd["bot_id"]);
auto bot = ref_server->music_manager_->findBotById(cmd["bot_id"]);
if(!bot) return command_result{error::music_invalid_id};
if(bot->getOwner() != this->getClientDatabaseId())
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, 0));
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
if(ref_server->musicManager->find_bot_by_playlist(playlist))
if(ref_server->music_manager_->find_bot_by_playlist(playlist))
return command_result{error::playlist_already_in_use};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power); perr)
return command_result{perr};
if(!ref_server->musicManager->assign_playlist(bot, playlist))
if(!ref_server->music_manager_->assign_playlist(bot, playlist))
return command_result{error::vs_critical};
return command_result{error::ok};
@ -1162,7 +1162,7 @@ command_result ConnectedClient::handleCommandPlaylistSetSubscription(ts::Command
if(!config::music::enabled) return command_result{error::music_disabled};
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
auto playlist = ref_server->music_manager_->find_playlist(cmd["playlist_id"]);
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
{

View File

@ -293,7 +293,7 @@ command_result ConnectedClient::handleCommandServerGroupList(Command &) {
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_list, 1);
this->notifyServerGroupList();
this->notifyServerGroupList(this->getType() != ClientType::CLIENT_QUERY);
this->command_times.servergrouplist = system_clock::now();
return command_result{error::ok};
}

View File

@ -416,8 +416,8 @@ command_result QueryClient::handleCommandServerInfo(Command &) {
if(this->server && permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_connectioninfo_view, 0))) {
auto total_stats = this->server->getServerStatistics()->total_stats();
auto report_second = this->server->serverStatistics->second_stats();
auto report_minute = this->server->serverStatistics->minute_stats();
auto report_second = this->server->server_statistics_->second_stats();
auto report_minute = this->server->server_statistics_->minute_stats();
cmd["connection_bandwidth_sent_last_second_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), (size_t) 0U);
cmd["connection_bandwidth_sent_last_minute_total"] = std::accumulate(report_minute.connection_bytes_sent.begin(), report_minute.connection_bytes_sent.end(), (size_t) 0U);
cmd["connection_bandwidth_received_last_second_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), (size_t) 0U);

View File

@ -118,8 +118,9 @@ CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInitIv(co
this->connection->packet_statistics().reset_offsets();
bool use_teaspeak = cmd.has_switch("teaspeak");
if(use_teaspeak ? !config::server::clients::teaspeak : !config::server::clients::teamspeak)
return command_result{error::client_type_is_not_allowed};
if(!use_teaspeak && !config::server::clients::teamspeak) {
return command_result{error::client_type_is_not_allowed, config::server::clients::teamspeak_not_allowed_message};
}
if(use_teaspeak) {
debugMessage(this->connection->virtual_server_id(), "{} Client using TeaSpeak.", this->connection->log_prefix());

View File

@ -8,8 +8,6 @@
#include <log/LogUtils.h>
#include "VoiceClient.h"
#include "src/VirtualServer.h"
#include "../../server/VoiceServer.h"
#include "src/InstanceHandler.h"
#include "src/manager/ActionLogger.h"
@ -277,4 +275,12 @@ float VoiceClient::current_ping_deviation() {
float VoiceClient::current_packet_loss() const {
return this->connection->packet_statistics().current_packet_loss();
}
}
void VoiceClient::processJoin() {
SpeakingClient::processJoin();
if(this->rtc_client_id > 0) {
auto sender = this->server->rtc_server().create_audio_source_supplier_sender(this->rtc_client_id);
assert(sender.has_value());
this->rtc_audio_supplier.reset(*sender);
}
}

View File

@ -96,12 +96,17 @@ namespace ts {
const VoicePacketFlags &flags
) override;
void processJoin() override;
protected:
virtual command_result handleCommand(Command &command) override;
private:
void finalDisconnect();
bool final_disconnected = false;
rtc::NativeAudioSourceSupplier rtc_audio_supplier{};
uint16_t stop_seq_counter{0};
//General TS3 manager commands
command_result handleCommandClientInit(Command&) override;
command_result handleCommandClientDisconnect(Command&);

View File

@ -220,6 +220,10 @@ void VoiceClientConnection::send_packet(protocol::PacketType type, protocol::Pac
this->packet_encoder_.send_packet(type, flag, payload, payload_size);
}
void VoiceClientConnection::send_packet(protocol::OutgoingServerPacket* packet) {
this->packet_encoder_.send_packet(packet);
}
void VoiceClientConnection::send_command(const std::string_view &cmd, bool b, std::unique_ptr<threads::Future<bool>> cb) {
this->packet_encoder_.send_command(cmd, b, std::move(cb));
}

View File

@ -56,6 +56,7 @@ namespace ts {
virtual ~VoiceClientConnection();
void send_packet(protocol::PacketType /* type */, protocol::PacketFlag::PacketFlags /* flags */, const void* /* payload */, size_t /* payload length */);
void send_packet(protocol::OutgoingServerPacket* /* packet */); /* method takes ownership of the packet */
void send_command(const std::string_view& /* build command command */, bool /* command low */, std::unique_ptr<threads::Future<bool>> /* acknowledge listener */);
CryptHandler* getCryptHandler(){ return &crypt_handler; }

View File

@ -28,7 +28,15 @@ void VoiceClientConnection::handlePacketVoice(const protocol::ClientPacketParser
auto client = this->getCurrentClient();
if(!client) return;
client->handlePacketVoice(packet.payload(), (packet.flags() & PacketFlag::Compressed) > 0, (packet.flags() & PacketFlag::Fragmented) > 0);
if(client->should_handle_voice_packet(packet.payload_length())) {
auto& sink = client->rtc_audio_supplier;
auto payload = packet.payload();
uint16_t vpacketId = be2le16((char*) payload.data_ptr());
auto codec = (uint8_t) payload[2];
sink.send_audio(vpacketId, false, vpacketId * 960, codec, std::string_view{payload.data_ptr<char>() + 3, payload.length() - 3});
}
}
void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) {

View File

@ -1,71 +0,0 @@
#include <cstring>
#include "SampleHandler.h"
using namespace std;
using namespace ts;
using namespace ts::sound;
SampleConverter::SampleConverter(size_t channelCount, size_t segmentSize) : channelCount(channelCount), segmentSize(segmentSize) { }
SampleConverter::~SampleConverter() = default;
void SampleConverter::pushSamples(float* pcm, size_t size) {
threads::MutexLock l(this->segmentLock);
size_t pcmIndex = 0;
if(!this->segments.empty()) {
if(!this->segments.back()->full) {
auto& last = this->segments.back();
if(last->sampleSize < this->segmentSize) { //Enought space
size_t pcmWrites = min(this->segmentSize - last->sampleSize, size);
auto buffer = static_cast<float *>(malloc(this->segmentSize * this->channelCount * sizeof(float)));
memcpy(buffer, last->samples, last->channelCount * last->sampleSize * sizeof(float));
free(last->samples);
memcpy((void*) &buffer[last->channelCount * last->sampleSize], pcm, pcmWrites * this->channelCount * sizeof(float));
pcmIndex += this->channelCount * pcmWrites;
last->sampleSize += pcmWrites * this->channelCount;
last->samples = buffer;
last->full = last->sampleSize == this->segmentSize;
}
}
}
while(size - (pcmIndex / this->channelCount) > this->segmentSize) {
auto segment = make_shared<SampleSegment>();
segment->channelCount = this->channelCount;
segment->sampleSize = this->segmentSize;
segment->full = true;
auto bufLen = this->segmentSize * this->channelCount * sizeof(float);
segment->samples = static_cast<float *>(malloc(bufLen));
memcpy(segment->samples, &pcm[pcmIndex], bufLen);
pcmIndex += bufLen / sizeof(float);
this->segments.push_back(segment);
}
auto overhead = size - (pcmIndex / this->channelCount);
if(overhead > 0){
auto segment = make_shared<SampleSegment>();
segment->channelCount = this->channelCount;
segment->sampleSize = overhead;
segment->full = false;
auto bufLen = overhead * this->channelCount * sizeof(float);
segment->samples = static_cast<float *>(malloc(bufLen));
memcpy(segment->samples, &pcm[pcmIndex], bufLen);
this->segments.push_back(segment);
}
}
std::shared_ptr<SampleSegment> SampleConverter::nextSegment() {
threads::MutexLock l(this->segmentLock);
if(this->segments.empty()) return nullptr;
if(!this->segments.front()->full && this->segments.size() == 1) return nullptr;
auto elm = std::move(this->segments.front());
this->segments.pop_front();
return elm;
}

View File

@ -1,42 +0,0 @@
#pragma once
#include <cstdlib>
#include <deque>
#include <memory>
#include <ThreadPool/Mutex.h>
namespace ts {
namespace sound {
struct SampleSegment {
float* samples;
size_t channelCount;
size_t sampleSize;
bool full = false;
SampleSegment(){}
~SampleSegment(){
if(samples) free(samples);
}
};
class SampleConverter {
public:
SampleConverter(size_t channelCount, size_t segmentSize);
~SampleConverter();
void pushSamples(float* pcm, size_t size);
std::shared_ptr<SampleSegment> nextSegment();
inline bool hasNext(){
threads::MutexLock l(this->segmentLock);
return !this->segments.empty();
}
size_t getSegmentSize() { return this->segmentSize; }
private:
size_t channelCount = 1;
size_t segmentSize = 0;
threads::Mutex segmentLock;
std::deque<std::shared_ptr<SampleSegment>> segments;
};
}
}

View File

@ -1,303 +0,0 @@
#include <misc/std_unique_ptr.h>
#include <log/LogUtils.h>
#include <misc/endianness.h>
#include <dlfcn.h>
#include "WebClient.h"
using namespace std;
using namespace ts;
using namespace ts::server;
using namespace ts::web;
void VoiceBridge::callback_log(void* ptr, pipes::Logger::LogLevel level, const std::string& name, const std::string& message, ...) {
auto max_length = 1024 * 8;
char buffer[max_length];
va_list args;
va_start(args, message);
max_length = vsnprintf(buffer, max_length, message.c_str(), args);
va_end(args);
auto bridge = (VoiceBridge*) ptr;
debugMessage(LOG_GENERAL, "{}[WebRTC][{}][{}] {}", CLIENT_STR_LOG_PREFIX_(bridge->owner()), level, name, string(buffer));
}
namespace gioloop {
void* main_loop_;
void*(*g_main_loop_new)(void* /* context */, bool /* is true */);
void(*g_main_loop_run)(void* /* loop */);
void(*g_main_loop_unref)(void* /* loop */);
void*(*g_main_loop_ref)(void* /* loop */);
bool initialized{false};
void initialize() {
if(initialized) return;
initialized = true;
g_main_loop_new = (decltype(g_main_loop_new)) dlsym(nullptr, "g_main_loop_new");
g_main_loop_run = (decltype(g_main_loop_run)) dlsym(nullptr, "g_main_loop_run");
g_main_loop_ref = (decltype(g_main_loop_ref)) dlsym(nullptr, "g_main_loop_ref");
g_main_loop_unref = (decltype(g_main_loop_unref)) dlsym(nullptr, "g_main_loop_unref");
if(!g_main_loop_run || !g_main_loop_new || !g_main_loop_ref || !g_main_loop_unref) {
logWarning(LOG_INSTANCE, "Missing g_main_loop_new, g_main_loop_run, g_main_loop_ref or g_main_loop_unref functions. Could not spawn main loop.");
g_main_loop_run = nullptr;
g_main_loop_new = nullptr;
return;
}
main_loop_ = g_main_loop_new(nullptr, false);
if(!main_loop_) {
logError(LOG_INSTANCE, "Failed to spawn new event loop for the web client.");
return;
}
std::thread([]{
g_main_loop_run(main_loop_);
}).detach();
}
std::shared_ptr<GMainLoop> loop() {
return std::shared_ptr<GMainLoop>{(GMainLoop*) g_main_loop_ref(main_loop_), g_main_loop_unref};
}
}
VoiceBridge::VoiceBridge(const shared_ptr<WebClient>& owner) : _owner(owner) {
auto config = make_shared<rtc::PeerConnection::Config>();
config->nice_config = make_shared<rtc::NiceWrapper::Config>();
config->nice_config->ice_port_range = {config::web::webrtc_port_min, config::web::webrtc_port_max};
if(config::web::stun_enabled)
config->nice_config->stun_server = { config::web::stun_host, config::web::stun_port };
config->nice_config->allow_ice_udp = config::web::udp_enabled;
config->nice_config->allow_ice_tcp = config::web::tcp_enabled;
config->nice_config->use_upnp = config::web::enable_upnp;
gioloop::initialize();
config->event_loop = gioloop::loop();
/*
config->nice_config->main_loop = std::shared_ptr<GMainLoop>(g_main_loop_new(nullptr, false), g_main_loop_unref);
std::thread(g_main_loop_run, config->nice_config->main_loop.get()).detach();
*/
config->logger = make_shared<pipes::Logger>();
config->logger->callback_log = VoiceBridge::callback_log;
config->logger->callback_argument = this;
//config->sctp.local_port = 5202; //Fire Fox don't support a different port :D
this->connection = make_unique<rtc::PeerConnection>(config) ;
}
VoiceBridge::~VoiceBridge() {
__asm__("nop");
}
int VoiceBridge::server_id() {
auto locked = this->_owner.lock();
return locked ? locked->getServerId() : 0;
}
std::shared_ptr<server::WebClient> VoiceBridge::owner() {
return this->_owner.lock();
}
bool VoiceBridge::initialize(std::string &error) {
if(!this->connection->initialize(error)) return false;
this->connection->callback_ice_candidate = [&](const rtc::IceCandidate& candidate) {
if(!candidate.is_finished_candidate()) {
if(auto callback{this->callback_ice_candidate}; callback)
callback(candidate);
} else {
if(auto callback{this->callback_ice_candidate_finished}; callback)
callback(candidate.sdpMid, candidate.sdpMLineIndex);
}
};
this->connection->callback_new_stream = [&](const std::shared_ptr<rtc::Channel> &channel) { this->handle_media_stream(channel); }; //bind(&VoiceBridge::handle_media_stream, this, placeholders::_1); => crash
this->connection->callback_setup_fail = [&](rtc::PeerConnection::ConnectionComponent comp, const std::string& reason) {
debugMessage(this->server_id(), "{} WebRTC setup failed! Component {} ({})", CLIENT_STR_LOG_PREFIX_(this->owner()), comp, reason);
if(this->callback_failed)
this->callback_failed();
};
return true;
}
bool VoiceBridge::parse_offer(const std::string &sdp) {
this->offer_timestamp = chrono::system_clock::now();
string error;
return this->connection->apply_offer(error, sdp);
}
int VoiceBridge::apply_ice(const std::deque<std::shared_ptr<rtc::IceCandidate>>& candidates) {
return this->connection->apply_ice_candidates(candidates);
}
void VoiceBridge::remote_ice_finished() {
this->connection->remote_candidates_finished();
}
std::string VoiceBridge::generate_answer() {
return this->connection->generate_answer(false);
}
void VoiceBridge::execute_tick() {
if(!this->voice_channel_) {
if(this->offer_timestamp.time_since_epoch().count() > 0 && this->offer_timestamp + chrono::seconds{20} < chrono::system_clock::now()) {
this->offer_timestamp = chrono::system_clock::time_point();
this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout");
}
}
}
void VoiceBridge::handle_media_stream(const std::shared_ptr<rtc::Channel> &undefined_stream) {
if(undefined_stream->type() == rtc::CHANTYPE_APPLICATION) {
auto stream = dynamic_pointer_cast<rtc::ApplicationChannel>(undefined_stream);
if(!stream) return;
stream->callback_datachannel_new = [&](const std::shared_ptr<rtc::DataChannel> &channel) { this->handle_data_channel(channel); }; //bind(&VoiceBridge::handle_data_channel, this, placeholders::_1); => may crash?
} else if(undefined_stream->type() == rtc::CHANTYPE_AUDIO) {
auto stream = dynamic_pointer_cast<rtc::AudioChannel>(undefined_stream);
if(!stream) return;
logTrace(this->server_id(), "Audio channel extensions:");
for(const auto& ex : stream->list_extensions()) {
logTrace(this->server_id(), " - {}: {}", ex->id, ex->name);
}
stream->register_local_extension("urn:ietf:params:rtp-hdrext:ssrc-audio-level");
for(const auto& codec : stream->list_codecs()) {
if(codec->type == rtc::codec::Codec::OPUS) {
codec->accepted = true;
break;
}
}
if(!this->incoming_voice_channel_.lock()) {
debugMessage(this->server_id(), "Having client's voice audio stream.");
this->incoming_voice_channel_ = stream;
stream->incoming_data_handler = [&](const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
this->handle_audio_voice_data(channel, data, payload_offset); };
} else if(!this->incoming_whisper_channel_.lock()) {
debugMessage(this->server_id(), "Having client's whispers audio stream.");
this->incoming_whisper_channel_ = stream;
stream->incoming_data_handler = [&](const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
this->handle_audio_voice_whisper_data(channel, data, payload_offset); };
} else {
debugMessage(this->server_id(), "Client sdp offer contains more than two voice channels.");
}
} else {
logError(this->server_id(), "Got offer for unknown channel of type {}", undefined_stream->type());
}
}
void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &channel) {
if(channel->lable() == "main" || channel->lable() == "voice") {
this->voice_channel_ = channel;
debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner()));
this->callback_initialized();
weak_ptr<rtc::DataChannel> weak_channel = channel;
channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) {
if(buffer.length() < 2)
return;
this->callback_voice_data(buffer.view(2), buffer[0] == 1);
};
channel->callback_close = [&, weak_channel] {
auto channel_ref = weak_channel.lock();
if(channel_ref == this->voice_channel_) {
this->voice_channel_ = nullptr;
//TODO may callback?
debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner()));
}
};
} else if(channel->lable() == "voice-whisper") {
this->voice_whisper_channel_ = channel;
debugMessage(this->server_id(), "{} Got voice whisper channel", CLIENT_STR_LOG_PREFIX_(this->owner()));
weak_ptr<rtc::DataChannel> weak_channel = channel;
channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) {
if(buffer.length() < 1)
return;
this->callback_voice_whisper_data(buffer.view(1), buffer[0] == 1);
};
channel->callback_close = [&, weak_channel] {
auto channel_ref = weak_channel.lock();
if(channel_ref == this->voice_whisper_channel_) {
this->voice_whisper_channel_ = nullptr;
debugMessage(this->server_id(), "{} Voice whisper channel has been closed.", CLIENT_STR_LOG_PREFIX_(this->owner()));
}
};
}
}
void VoiceBridge::handle_audio_voice_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
if(channel->codec->type != rtc::codec::Codec::OPUS) {
//debugMessage(this->server_id(), "{} Got unknown codec ({})!", CLIENT_STR_LOG_PREFIX_(this->owner()), channel->codec->type);
return;
}
this->handle_audio_voice_x_data(&this->voice_state, data, payload_offset);
}
void VoiceBridge::handle_audio_voice_whisper_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {
if(channel->codec->type != rtc::codec::Codec::OPUS) {
return;
}
this->handle_audio_voice_x_data(&this->whisper_state, data, payload_offset);
}
void VoiceBridge::handle_audio_voice_x_data(VoiceStateData *state, const pipes::buffer_view &data, size_t payload_offset) {
bool is_silence{false};
auto audio_channel = state->channel.lock();
if(!audio_channel) {
return;
}
for(const auto& ext : audio_channel->list_extensions(rtc::direction::incoming)) {
if(ext->name == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") {
int level;
if(rtc::protocol::rtp_header_extension_parse_audio_level(data, ext->id, &level) == 0) {
//debugMessage(this->server_id(), "Audio level: {}", level);
if(level == 127) {
is_silence = true;
break;
}
}
break;
}
}
if(is_silence) {
if(state->muted) {
/* the muted state is already set */
return;
}
state->muted = true;
auto target_buffer = buffer::allocate_buffer(3);
le2be16(state->sequence_packet_id++, (char*) target_buffer.data_ptr());
target_buffer[2] = 5;
state->callback(target_buffer, false);
} else {
if(state->muted) {
state->muted = false;
}
auto target_buffer = buffer::allocate_buffer(data.length() - payload_offset + 3);
le2be16(state->sequence_packet_id++, (char*) target_buffer.data_ptr());
target_buffer[2] = 5;
memcpy(&target_buffer[3], &data[payload_offset], data.length() - payload_offset);
state->callback(target_buffer, state->sequence_packet_id < 7);
}
}

View File

@ -1,82 +0,0 @@
#pragma once
#include <pipes/rtc/PeerConnection.h>
#include <pipes/rtc/channels/ApplicationChannel.h>
#include <pipes/rtc/channels/AudioChannel.h>
namespace ts {
namespace server {
class WebClient;
}
namespace web {
class VoiceBridge {
public:
typedef std::function<void(const pipes::buffer_view&, bool /* is sequence start */)> cb_voice_data;
typedef std::function<void(const rtc::IceCandidate&)> cb_ice_candidate;
typedef std::function<void(const std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
typedef std::function<void()> cb_initialized;
typedef std::function<void()> cb_failed;
std::shared_ptr<rtc::DataChannel> voice_channel() { return this->voice_channel_; }
std::shared_ptr<rtc::DataChannel> voice_whisper_channel() { return this->voice_whisper_channel_; }
explicit VoiceBridge(const std::shared_ptr<server::WebClient>&);
virtual ~VoiceBridge();
bool initialize(std::string&);
bool parse_offer(const std::string&);
int apply_ice(const std::deque<std::shared_ptr<rtc::IceCandidate>>& /* candidates */);
void remote_ice_finished();
std::string generate_answer();
cb_ice_candidate callback_ice_candidate;
cb_ice_candidate_finish callback_ice_candidate_finished;
cb_voice_data callback_voice_data;
cb_voice_data callback_voice_whisper_data;
cb_initialized callback_initialized;
cb_failed callback_failed;
void execute_tick();
private:
struct VoiceStateData {
uint16_t sequence_packet_id{0};
bool muted{true};
std::weak_ptr<rtc::AudioChannel>& channel;
cb_voice_data& callback;
};
static void callback_log(void* ptr, pipes::Logger::LogLevel level, const std::string& name, const std::string& message, ...);
inline int server_id();
inline std::shared_ptr<server::WebClient> owner();
void handle_media_stream(const std::shared_ptr<rtc::Channel>& /* stream */);
void handle_data_channel(const std::shared_ptr<rtc::DataChannel> & /* channel */);
void handle_audio_voice_data(const std::shared_ptr<rtc::MediaChannel>& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */);
void handle_audio_voice_whisper_data(const std::shared_ptr<rtc::MediaChannel>& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */);
static void handle_audio_voice_x_data(VoiceStateData* /* state */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */);
std::weak_ptr<server::WebClient> _owner;
std::chrono::system_clock::time_point offer_timestamp;
std::unique_ptr<rtc::PeerConnection> connection;
std::shared_ptr<rtc::DataChannel> voice_channel_{};
std::shared_ptr<rtc::DataChannel> voice_whisper_channel_{};
std::weak_ptr<rtc::AudioChannel> incoming_voice_channel_{};
std::weak_ptr<rtc::AudioChannel> incoming_whisper_channel_{};
VoiceStateData voice_state{
.channel = this->incoming_voice_channel_,
.callback = this->callback_voice_data
};
VoiceStateData whisper_state{
.channel = this->incoming_whisper_channel_,
.callback = this->callback_voice_whisper_data
};
};
}
}

View File

@ -23,7 +23,7 @@ void WebClient::handleMessageWrite(int fd, short, void *) {
buffer_lock.unlock();
if (errno == EINTR || errno == EAGAIN) {
lock_guard event_lock(this->event_lock);
lock_guard event_lock(this->event_mutex);
if(this->writeEvent)
event_add(this->writeEvent, nullptr);
return;
@ -31,10 +31,12 @@ void WebClient::handleMessageWrite(int fd, short, void *) {
//new ServerConnection(globalClient).startConnection({ host: "localhost", port: 9987}, new HandshakeHandler(profiles.default_profile(), "test"))
{
lock_guard event_lock(this->event_lock);
event_del_noblock(this->writeEvent);
event_free(this->writeEvent);
this->writeEvent = nullptr;
std::lock_guard event_lock{this->event_mutex};
if(this->writeEvent) {
event_del_noblock(this->writeEvent);
event_free(this->writeEvent);
this->writeEvent = nullptr;
}
}
debugMessage(this->getServerId(), "[{}] Failed to write message (length {}, errno {}, message {}) Disconnecting client.", CLIENT_STR_LOG_PREFIX, written, errno, strerror(errno));
}
@ -53,7 +55,7 @@ void WebClient::handleMessageWrite(int fd, short, void *) {
/* reschedule new write */
buffer_lock.unlock();
lock_guard event_lock(this->event_lock);
lock_guard event_lock(this->event_mutex);
if(this->writeEvent)
event_add(this->writeEvent, nullptr);
}
@ -72,7 +74,7 @@ void WebClient::handleMessageRead(int fd, short, void *) {
debugMessage(this->getServerId(), "[{}] Failed to read message (length {}, errno {}, message: {}). Closing connection.", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno));
{
lock_guard lock(this->event_lock);
lock_guard lock(this->event_mutex);
if(this->readEvent)
event_del_noblock(this->readEvent);
}
@ -99,7 +101,7 @@ void WebClient::enqueue_raw_packet(const pipes::buffer_view &msg) {
this->queue_write.push_back(buffer);
}
{
lock_guard lock(this->event_lock);
lock_guard lock(this->event_mutex);
if(this->writeEvent)
event_add(this->writeEvent, nullptr);
}

View File

@ -275,11 +275,7 @@ command_result WebClient::handleCommand(Command &command) {
void WebClient::tick(const std::chrono::system_clock::time_point& point) {
SpeakingClient::tick(point);
{
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(this->voice_bridge)
this->voice_bridge->execute_tick();
}
if(this->ping.last_request + seconds(1) < point) {
if(this->ping.last_response > this->ping.last_request || this->ping.last_response + this->ping.timeout < point) {
this->ping.current_id++;
@ -390,7 +386,7 @@ void WebClient::disconnectFinal() {
{
::event *event_read, *event_write;
{
unique_lock event_lock(this->event_lock);
unique_lock event_lock(this->event_mutex);
event_read = this->readEvent;
event_write = this->writeEvent;
@ -417,14 +413,6 @@ void WebClient::disconnectFinal() {
}
this->processLeave();
{
unique_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(this->voice_bridge) {
//TODO correct close?
this->voice_bridge = nullptr;
}
}
this->ssl_handler.finalize();
this->handle->unregisterConnection(static_pointer_cast<WebClient>(self_lock));
@ -470,179 +458,6 @@ void WebClient::handleMessage(const pipes::buffer_view &message) {
}
this->handleCommandFull(cmd, true);
} else if(val["type"].asString() == "WebRTC") {
auto subType = val["request"].asString();
if(subType == "create") {
std::unique_lock voice_bridge_lock_{this->voice_bridge_lock};
if(this->voice_bridge) {
logError(this->server->getServerId(), "[{}] Tried to register a WebRTC channel twice!", CLIENT_STR_LOG_PREFIX_(this));
std::thread([&, vb_ptr = std::move(this->voice_bridge), lock = this->ref()]() mutable {
vb_ptr = nullptr;
lock = nullptr;
}).detach();
}
this->voice_bridge = make_unique<web::VoiceBridge>(dynamic_pointer_cast<WebClient>(this->ref())); //FIXME Add config
this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool head) {
/* may somehow get the "real" packet size? */
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
this->handlePacketVoice(buffer, head, false);
};
this->voice_bridge->callback_voice_whisper_data = [&](const pipes::buffer_view& buffer, bool head) {
/* may somehow get the "real" packet size? */
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
constexpr static auto kTempBufferSize{2048};
char temp_buffer[kTempBufferSize];
size_t offset{0};
/* copy the voice header */
memcpy(temp_buffer, buffer.data_ptr(), 3);
offset += 3;
bool is_new;
{
std::lock_guard whisper_header_lock{this->whisper.mutex};
if(!this->whisper.is_set) {
return;
}
memcpy(temp_buffer + offset, this->whisper.target_header.data_ptr(), this->whisper.target_header.length());
offset += this->whisper.target_header.length();
is_new = this->whisper.is_new_header;
}
memcpy(temp_buffer + offset, buffer.data_ptr<char>() + 3, buffer.length() - 3);
offset += buffer.length() - 3;
this->handlePacketVoiceWhisper(pipes::buffer_view{temp_buffer, offset}, is_new, head);
};
this->voice_bridge->callback_initialized = [&](){
debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX);
};
this->voice_bridge->callback_failed = [&] {
auto vb_ptr = &*this->voice_bridge; /* read only no lock needed */
std::thread([&, vb_ptr, lock = this->ref()]{
unique_lock vbl{this->voice_bridge_lock};
if(&*this->voice_bridge == vb_ptr) {
auto bridge = std::exchange(this->voice_bridge, nullptr);
vbl.unlock();
bridge.reset();
}
}).detach();
Json::Value response;
response["type"] = "WebRTC";
response["request"] = "status";
response["state"] = "failed";
response["reason"] = "voice bridge setup failed!";
response["allow_reconnect"] = true;
this->sendJson(response);
};
this->voice_bridge->callback_ice_candidate = [&](const rtc::IceCandidate& ice) {
Json::Value jsonCandidate;
jsonCandidate["type"] = "WebRTC";
jsonCandidate["request"] = "ice";
jsonCandidate["msg"]["candidate"] = ice.candidate;
jsonCandidate["msg"]["sdpMid"] = ice.sdpMid;
jsonCandidate["msg"]["sdpMLineIndex"] = ice.sdpMLineIndex;
this->sendJson(jsonCandidate);
};
this->voice_bridge->callback_ice_candidate_finished = [&](const std::string& sdpMid, int sdpMLineIndex){
Json::Value jsonCandidate;
jsonCandidate["type"] = "WebRTC";
jsonCandidate["request"] = "ice_finish";
jsonCandidate["msg"]["candidate"] = "";
jsonCandidate["msg"]["sdpMid"] = sdpMid;
jsonCandidate["msg"]["sdpMLineIndex"] = sdpMLineIndex;
this->sendJson(jsonCandidate);
};
auto vbp = &*this->voice_bridge;
voice_bridge_lock_.unlock();
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(vbp != &*this->voice_bridge) {
Json::Value response;
response["type"] = "WebRTC";
response["request"] = "status";
response["state"] = "failed";
response["reason"] = "initialize failed (obsolete bridge)";
response["allow_reconnect"] = true;
this->sendJson(response);
return;
}
string error;
if(!this->voice_bridge->initialize(error)) {
Json::Value response;
response["type"] = "WebRTC";
response["request"] = "status";
response["state"] = "failed";
response["reason"] = "initialize failed (" + error + ")";
response["allow_reconnect"] = true;
this->sendJson(response);
return;
}
if(!this->voice_bridge->parse_offer(val["msg"]["sdp"].asString())) {
Json::Value response;
response["type"] = "WebRTC";
response["request"] = "status";
response["state"] = "failed";
response["reason"] = "offer apply failed (" + error + ")";
response["allow_reconnect"] = true;
this->sendJson(response);
return;
} else {
auto sdp_response = this->voice_bridge->generate_answer();
Json::Value response;
response["type"] = "WebRTC";
response["request"] = "answer";
response["msg"]["sdp"] = sdp_response;
response["msg"]["type"] = "answer";
this->sendJson(response);
}
} else if(subType == "ice") {
std::shared_lock read_voice_bridge_lock{this->voice_bridge_lock};
if(!this->voice_bridge) {
debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX);
return;
} else {
auto candidate_string = val["msg"]["candidate"].asString();
auto sdp_mid = val["msg"]["sdpMid"].asString();
auto sdp_line_index = val["msg"]["sdpMLineIndex"].asInt();
deque<shared_ptr<rtc::IceCandidate>> candidates;
candidates.push_back(make_shared<rtc::IceCandidate>(candidate_string, sdp_mid, sdp_line_index));
auto result = this->voice_bridge->apply_ice(candidates);
if(result != candidates.size()) {
logError(this->getServerId(),
"[{}] Failed to apply remote ICE candidate, result: {}. Channel: {} ({}). Candidate: {}",
CLIENT_STR_LOG_PREFIX_(this),
result,
sdp_line_index,
sdp_mid,
candidate_string
);
} else {
logTrace(this->getServerId(), "[{}] Successfully added ICE candidate for channel {} ({}).", CLIENT_STR_LOG_PREFIX_(this), sdp_line_index, sdp_mid);
}
}
} else if(subType == "ice_finish") {
std::shared_lock read_voice_bridge_lock{this->voice_bridge_lock};
if(!this->voice_bridge) {
debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX);
return;
}
this->voice_bridge->remote_ice_finished();
}
} else if(val["type"].asString() == "ping") {
Json::Value response;
response["type"] = "pong";
@ -703,50 +518,17 @@ bool WebClient::disconnect(const std::string &reason) {
command_result WebClient::handleCommandClientInit(Command &command) {
if(!config::server::clients::teaweb)
return command_result{error::client_type_is_not_allowed};
return command_result{error::client_type_is_not_allowed, config::server::clients::teaweb_not_allowed_message};
return SpeakingClient::handleCommandClientInit(command);
}
bool WebClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false;
return SpeakingClient::shouldReceiveVoice(sender);
}
void WebClient::send_voice_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(this->voice_bridge) {
auto channel = this->voice_bridge->voice_channel();
if(channel) {
channel->send(view);
read_voice_bridge_lock.unlock();
/* may somehow get the "real" packet size? */
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length());
}
}
/* Should never be called! */
}
void WebClient::send_voice_whisper_packet(const pipes::buffer_view &, const pipes::buffer_view &teaspeak_packet, const SpeakingClient::VoicePacketFlags &flags) {
std::shared_lock read_voice_bridge_lock{this->voice_bridge_lock};
if(this->voice_bridge) {
auto channel = this->voice_bridge->voice_whisper_channel();
if(channel) {
uint8_t buffer[teaspeak_packet.length() + 1];
memcpy(buffer + 1, teaspeak_packet.data_ptr(), teaspeak_packet.length());
buffer[0] = 0;
if(flags.head) {
buffer[0] |= 0x1U;
}
channel->send(pipes::buffer{buffer, teaspeak_packet.length() + 1});
read_voice_bridge_lock.unlock();
/* may somehow get the "real" packet size? */
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, teaspeak_packet.length());
}
}
/* Should never be called! */
}
command_result WebClient::handleCommandSetWhisperTarget(Command &command) {

View File

@ -8,9 +8,7 @@
#include <src/client/ConnectedClient.h>
#include <protocol/buffers.h>
#include "misc/queue.h"
#include "SampleHandler.h"
#include <opus/opus.h>
#include "VoiceBridge.h"
#include <json/json.h>
#include <EventLoop.h>
@ -30,8 +28,6 @@ namespace ts::server {
bool disconnect(const std::string &reason) override;
bool close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override;
bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) override;
[[nodiscard]] inline std::chrono::nanoseconds client_ping() const { return this->client_ping_layer_7(); }
[[nodiscard]] inline std::chrono::nanoseconds client_ping_layer_5() const { return this->ping.value; }
@ -44,8 +40,6 @@ namespace ts::server {
private:
WebControlServer* handle;
std::shared_mutex voice_bridge_lock;
std::unique_ptr<web::VoiceBridge> voice_bridge;
int file_descriptor;
bool allow_raw_commands{false};
@ -54,7 +48,7 @@ namespace ts::server {
pipes::SSL ssl_handler;
pipes::WebSocket ws_handler;
std::mutex event_lock;
std::mutex event_mutex;
::event* readEvent;
::event* writeEvent;

View File

@ -44,7 +44,7 @@ if(!result && result.msg().find(ignore) == string::npos){
#define RESIZE_COLUMN(tblName, rowName, size) up vote EXECUTE("Could not change column size", "ALTER TABLE " tblName " ALTER COLUMN " rowName " varchar(" size ")");
#define CURRENT_DATABASE_VERSION 16
#define CURRENT_DATABASE_VERSION 17
#define CURRENT_PERMISSION_VERSION 5
#define CLIENT_UID_LENGTH "64"
@ -567,6 +567,17 @@ bool SqlDataManager::update_database(std::string &error) {
db_version(16);
}
case 16: {
constexpr static std::array<std::string_view, 1> kUpdateCommands{
"UPDATE `properties` SET `value` = '4' WHERE `key` = 'channel_codec' AND (`value` != '4' OR `value` != '5');",
};
if(!execute_commands(this->sql(), error, kUpdateCommands))
return false;
db_version(17);
}
default:
break;
}

View File

@ -19,7 +19,7 @@ threads::ThreadPool MusicBotManager::load_music{4, "music loader "};
void MusicBotManager::adjustTickPool() {
size_t bots = 0;
for(const auto& server : serverInstance->getVoiceServerManager()->serverInstances()) bots += server->musicManager->current_bot_count();
for(const auto& server : serverInstance->getVoiceServerManager()->serverInstances()) bots += server->music_manager_->current_bot_count();
if(bots == 0)
tick_music.setThreads(1);
else

57
server/src/rtc/imports.h Normal file
View File

@ -0,0 +1,57 @@
#include <cstdint>
#include <cstdlib>
#ifdef __cplusplus
extern "C" {
#endif
/* Attention: Do not call any librtc functions within being in the callback, only librtc_destroy_client is allowed */
struct NativeCallbacks {
uint32_t version;
void(*free_client_data)(const void*);
void(*client_stream_assignment)(const void* /* callback data */, uint32_t /* stream id */, const void* /* source callback data */);
void(*client_offer_generated)(const void* /* callback data */, const char* /* offer */, size_t /* offer length */);
void(*client_stream_start)(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 */);
};
extern const char* librtc_version();
extern void librtc_free_str(const char* /* ptr */);
extern const char* librtc_init(const NativeCallbacks* /* */, size_t /* size of the callback struct */);
extern void* librtc_create_server();
extern void librtc_destroy_server(void* /* server */);
extern uint32_t librtc_create_rtp_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 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_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 void* librtc_create_audio_source_supplier(void* /* server */, uint32_t /* client id */);
extern void librtc_audio_source_supply(void* /* sender */,
uint16_t /* seq no */,
bool /* marked */,
uint32_t /* timestamp */,
uint8_t /* codec */,
const void* /* data */,
uint32_t /* length */);
extern void librtc_destroy_audio_source_supplier(void* /* sender */);
extern uint32_t librtc_create_channel(void* /* server */);
extern uint32_t librtc_assign_channel(void* /* server */, uint32_t /* client id */, uint32_t /* channel 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 */);
#ifdef __cplusplus
};
#endif

281
server/src/rtc/lib.cpp Normal file
View File

@ -0,0 +1,281 @@
//
// Created by WolverinDEV on 24/10/2020.
//
#include "./lib.h"
#include "./imports.h"
#include "../client/SpeakingClient.h"
#include "../client/voice/VoiceClient.h"
#include <Definitions.h>
using namespace ts;
using namespace ts::server;
using namespace ts::rtc;
struct LibCallbackData {
ClientId client_id;
std::weak_ptr<SpeakingClient> weak_ref;
};
void librtc_callback_free_client_data(const void* data) {
delete (LibCallbackData*) data;
}
void librtc_callback_client_stream_assignment(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
auto callback_data = (LibCallbackData*) callback_data_ptr;
auto source_data = (LibCallbackData*) source_data_ptr;
auto target_client = callback_data->weak_ref.lock();
if(!target_client) { return; }
if(source_data) {
auto source_client = source_data->weak_ref.lock();
if(!source_client) { return; }
ts::command_builder notify{"notifyrtcstreamassignment"};
notify.put_unchecked(0, "streamid", stream_id);
notify.put_unchecked(0, "sclid", source_client->getClientId());
notify.put_unchecked(0, "scluid", source_client->getUid());
notify.put_unchecked(0, "scldbid", source_client->getClientDatabaseId());
notify.put_unchecked(0, "sclname", source_client->getDisplayName());
target_client->sendCommand(notify);
} else {
ts::command_builder notify{"notifyrtcstreamassignment"};
notify.put_unchecked(0, "streamid", stream_id);
notify.put_unchecked(0, "sclid", 0);
target_client->sendCommand(notify);
}
}
void librtc_callback_client_offer_generated(const void* callback_data_ptr, const char* offer, size_t offer_length) {
auto callback_data = (LibCallbackData*) callback_data_ptr;
auto target_client = callback_data->weak_ref.lock();
if(!target_client) { return; }
ts::command_builder notify{"notifyrtcsessiondescription"};
notify.put_unchecked(0, "mode", "offer");
notify.put_unchecked(0, "sdp", std::string_view{offer, offer_length});
target_client->sendCommand(notify);
}
void librtc_callback_client_audio_start(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
auto callback_data = (LibCallbackData*) callback_data_ptr;
auto source_data = (LibCallbackData*) source_data_ptr;
auto target_client = callback_data->weak_ref.lock();
if(!target_client) { return; }
auto source_client = source_data->weak_ref.lock();
if(!source_client) { return; }
ts::command_builder notify{"notifyrtcstateaudio"};
notify.put_unchecked(0, "streamid", stream_id);
notify.put_unchecked(0, "sclid", source_client->getClientId());
notify.put_unchecked(0, "scluid", source_client->getUid());
notify.put_unchecked(0, "scldbid", source_client->getClientDatabaseId());
notify.put_unchecked(0, "sclname", source_client->getDisplayName());
notify.put_unchecked(0, "state", "1");
target_client->sendCommand(notify);
}
void librtc_callback_client_audio_stop(const void* callback_data_ptr, uint32_t stream_id, const void* source_data_ptr) {
auto callback_data = (LibCallbackData*) callback_data_ptr;
auto source_data = (LibCallbackData*) source_data_ptr;
auto target_client = callback_data->weak_ref.lock();
if(!target_client) { return; }
auto source_client = source_data->weak_ref.lock();
if(!source_client) { return; }
ts::command_builder notify{"notifyrtcstateaudio"};
notify.put_unchecked(0, "streamid", stream_id);
notify.put_unchecked(0, "sclid", source_client->getClientId());
notify.put_unchecked(0, "state", "0");
target_client->sendCommand(notify);
}
void librtc_callback_client_audio_sender_data(const void* callback_data_ptr, const void* source_data_ptr, uint8_t mode, uint16_t seq_no, uint8_t codec, const void* data, uint32_t length) {
auto callback_data = (LibCallbackData*) callback_data_ptr;
auto source_data = (LibCallbackData*) source_data_ptr;
/* Target client must be a voice client. The web client does not uses the native audio client */
auto target_client = std::dynamic_pointer_cast<VoiceClient>(callback_data->weak_ref.lock());
if(!target_client) { return; }
auto source_client = source_data->weak_ref.lock();
if(!source_client) { return; }
if(mode == 0) {
if(!target_client->shouldReceiveVoice(source_client)) {
return;
}
/* TODO: Somehow set the head (compressed) flag for beginning voice packets? */
auto packet = protocol::allocate_outgoing_packet(length + 5);
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)) {
return;
}
/* FIXME: TODO! */
}
}
static NativeCallbacks native_callbacks{
.version = 1,
.free_client_data = librtc_callback_free_client_data,
.client_stream_assignment = librtc_callback_client_stream_assignment,
.client_offer_generated = librtc_callback_client_offer_generated,
.client_stream_start = librtc_callback_client_audio_start,
.client_stream_stop = librtc_callback_client_audio_stop,
.client_audio_sender_data = librtc_callback_client_audio_sender_data
};
std::string_view rtc::version() {
return std::string_view{librtc_version()};
}
bool rtc::initialize(std::string &error) {
auto error_ptr = librtc_init(&native_callbacks, sizeof native_callbacks);
if(!error_ptr) { return true; }
error = std::string{error_ptr};
librtc_free_str(error_ptr);
return false;
}
Server::Server() {
this->server_ptr = librtc_create_server();
}
Server::~Server() {
librtc_destroy_server(this->server_ptr);
}
RTCClientId Server::create_rtp_client(const std::shared_ptr<server::SpeakingClient> &client) {
auto data = new LibCallbackData{
.client_id = client->getClientId(),
.weak_ref = client
};
return librtc_create_rtp_client(this->server_ptr, data);
}
RTCClientId Server::create_native_client(const std::shared_ptr<server::SpeakingClient> &client) {
auto data = new LibCallbackData{
.client_id = client->getClientId(),
.weak_ref = client
};
return librtc_create_native_client(this->server_ptr, data);
}
void Server::destroy_client(RTCClientId client_id) {
librtc_destroy_client(this->server_ptr, client_id);
}
void Server::reset_rtp_session(RTCClientId client_id) {
librtc_reset_rtp_session(this->server_ptr, client_id);
}
bool Server::apply_remote_description(std::string &error, RTCClientId client_id, uint32_t mode, const std::string_view &description) {
auto error_ptr = librtc_apply_remote_description(this->server_ptr, client_id, mode, description.data());
if(!error_ptr) { return true; }
error = std::string{error_ptr};
librtc_free_str(error_ptr);
return false;
}
bool Server::generate_local_description(RTCClientId client, std::string &result) {
char* description_ptr;
auto error_ptr = librtc_generate_local_description(this->server_ptr, client, &description_ptr);
if(error_ptr) {
result = std::string{error_ptr};
librtc_free_str(error_ptr);
return false;
} else {
result = std::string{description_ptr};
librtc_free_str(description_ptr);
return true;
}
}
bool Server::add_ice_candidate(std::string &error, RTCClientId client_id, uint32_t media_line, const std::string_view &description) {
auto error_ptr = librtc_add_ice_candidate(this->server_ptr, client_id, media_line, description.length() == 0 ? nullptr : description.data());
if(!error_ptr) { return true; }
error = std::string{error_ptr};
librtc_free_str(error_ptr);
return false;
}
void Server::ice_candidates_finished(RTCClientId) {
/* Nothing really to do here */
}
uint32_t Server::create_channel() {
return librtc_create_channel(this->server_ptr);
}
ChannelAssignResult Server::assign_channel(uint32_t client_id, uint32_t channel_id) {
auto result = librtc_assign_channel(this->server_ptr, client_id, channel_id);
switch(result) {
case 0x00: return ChannelAssignResult::Success;
case 0x01: return ChannelAssignResult::ClientUnknown;
case 0x02: return ChannelAssignResult::TargetChannelUnknown;
default: return ChannelAssignResult::UnknownError;
}
}
BroadcastStartResult Server::start_broadcast(uint32_t client_id, uint8_t btype, uint32_t track_id) {
auto result = librtc_client_broadcast(this->server_ptr, client_id, btype, track_id);
switch(result) {
case 0x00: return BroadcastStartResult::Success;
case 0x01: return BroadcastStartResult::InvalidClient;
case 0x02: return BroadcastStartResult::ClientHasNoChannel;
case 0x03: return BroadcastStartResult::InvalidBroadcastType;
case 0x04: return BroadcastStartResult::InvalidStreamId;
default: return BroadcastStartResult::UnknownError;
}
}
std::optional<NativeAudioSourceSupplier> Server::create_audio_source_supplier_sender(uint32_t client_id) {
auto result = librtc_create_audio_source_supplier(this->server_ptr, client_id);
if(!result) { return std::nullopt; }
return std::make_optional<NativeAudioSourceSupplier>(result);
}
void Server::destroy_channel(uint32_t channel_id) {
librtc_destroy_channel(this->server_ptr, channel_id);
}
NativeAudioSourceSupplier::NativeAudioSourceSupplier(void *ptr) : sender_ptr{ptr} {}
NativeAudioSourceSupplier::NativeAudioSourceSupplier(NativeAudioSourceSupplier &&other) noexcept : sender_ptr{other.sender_ptr} {
other.sender_ptr = nullptr;
}
NativeAudioSourceSupplier::~NativeAudioSourceSupplier() noexcept {
if(this->sender_ptr) {
librtc_destroy_audio_source_supplier(this->sender_ptr);
}
}
void NativeAudioSourceSupplier::send_audio(uint16_t seq_no, bool marked, uint32_t timestamp, uint8_t codec, const std::string_view &data) {
librtc_audio_source_supply(this->sender_ptr, seq_no, marked, timestamp, codec, data.empty() ? nullptr : data.data(), data.length());
}

80
server/src/rtc/lib.h Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include <memory>
#include <optional>
#include <string>
namespace ts::server {
class SpeakingClient;
}
namespace ts::rtc {
typedef uint32_t RTCClientId;
typedef uint32_t RTCChannelId;
typedef uint32_t RTCStreamId;
extern std::string_view version();
extern bool initialize(std::string& /* error */);
enum struct ChannelAssignResult {
Success,
ClientUnknown,
TargetChannelUnknown,
UnknownError
};
enum struct BroadcastStartResult {
Success,
InvalidClient,
ClientHasNoChannel,
InvalidBroadcastType,
InvalidStreamId,
UnknownError
};
class NativeAudioSourceSupplier;
class Server {
public:
Server();
~Server();
RTCClientId create_rtp_client(const std::shared_ptr<server::SpeakingClient>& /* client */);
RTCClientId create_native_client(const std::shared_ptr<server::SpeakingClient>& /* client */);
void destroy_client(RTCClientId /* client id */);
/* RTC client actions */
void reset_rtp_session(RTCClientId /* client */);
bool apply_remote_description(std::string& /* error */, RTCClientId /* client id */, uint32_t /* mode */, const std::string_view& /* description */);
bool generate_local_description(RTCClientId /* client id */, std::string& /* result */);
bool add_ice_candidate(std::string& /* error */, RTCClientId /* client id */, uint32_t /* media line */, const std::string_view& /* description */);
void ice_candidates_finished(RTCClientId /* client id */);
/* Native client actions */
std::optional<NativeAudioSourceSupplier> create_audio_source_supplier_sender(RTCClientId /* client id */);
/* channel actions */
uint32_t create_channel();
ChannelAssignResult assign_channel(RTCClientId /* client id */, RTCChannelId /* channel id */);
BroadcastStartResult start_broadcast(RTCClientId /* client id */, uint8_t /* broadcast type */, RTCStreamId /* stream id */);
void destroy_channel(RTCChannelId /* channel id */);
private:
void* server_ptr{nullptr};
};
class NativeAudioSourceSupplier {
public:
explicit NativeAudioSourceSupplier() : sender_ptr{nullptr} {}
explicit NativeAudioSourceSupplier(void*);
virtual ~NativeAudioSourceSupplier() noexcept;
NativeAudioSourceSupplier(const NativeAudioSourceSupplier&) = delete;
NativeAudioSourceSupplier(NativeAudioSourceSupplier&& other) noexcept;
inline void reset(NativeAudioSourceSupplier& other) {
std::swap(other.sender_ptr, this->sender_ptr);
}
void send_audio(uint16_t /* seq no */, bool /* marked */, uint32_t /* timestamp */, uint8_t /* codec */, const std::string_view& /* data */);
private:
void* sender_ptr;
};
}

View File

@ -115,7 +115,9 @@ void VoiceServer::triggerWrite(const std::shared_ptr<VoiceClient>& client) {
return;
}
this->io->invoke_write(client);
if(auto io{this->io}; io) {
io->invoke_write(client);
}
}
void VoiceServer::schedule_command_handling(const ts::server::VoiceClient *client) {

2
shared

@ -1 +1 @@
Subproject commit f5c643129e306132437e8b529c2398e9cfc0545a
Subproject commit 7b51bcc5e82802ea8173da8c341ff31614f1c49b