From a37ba81a4f9a436d3aea9971b5d6c1a4cf9f05be Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 7 Nov 2020 13:17:51 +0100 Subject: [PATCH] Initial video commit --- .clang-format | 66 ++++ .gitmodules | 3 + git-teaspeak | 2 +- package.json | 8 - rtclib | 1 + server/CMakeLists.txt | 24 +- server/main.cpp | 26 +- server/src/Configuration.cpp | 16 +- server/src/Configuration.h | 3 +- server/src/TS3ServerClientManager.cpp | 23 +- server/src/TS3ServerHeartbeat.cpp | 6 +- server/src/VirtualServer.cpp | 27 +- server/src/VirtualServer.h | 16 +- server/src/VirtualServerManager.cpp | 2 +- server/src/channel/ServerChannel.cpp | 60 ++-- server/src/channel/ServerChannel.h | 18 +- server/src/client/ConnectedClient.h | 4 +- .../client/ConnectedClientNotifyHandler.cpp | 8 +- .../ConnectedClientTextCommandHandler.cpp | 4 +- server/src/client/SpeakingClient.cpp | 229 ++++++++----- server/src/client/SpeakingClient.h | 28 +- server/src/client/command_handler/channel.cpp | 6 +- server/src/client/command_handler/misc.cpp | 1 + server/src/client/command_handler/music.cpp | 64 ++-- server/src/client/command_handler/server.cpp | 2 +- .../src/client/query/QueryClientCommands.cpp | 4 +- server/src/client/voice/CryptSetupHandler.cpp | 5 +- server/src/client/voice/VoiceClient.cpp | 12 +- server/src/client/voice/VoiceClient.h | 5 + .../client/voice/VoiceClientConnection.cpp | 4 + .../src/client/voice/VoiceClientConnection.h | 1 + .../VoiceClientConnectionPacketHandler.cpp | 10 +- server/src/client/web/SampleHandler.cpp | 71 ---- server/src/client/web/SampleHandler.h | 42 --- server/src/client/web/VoiceBridge.cpp | 303 ------------------ server/src/client/web/VoiceBridge.h | 82 ----- server/src/client/web/WSWebClient.cpp | 18 +- server/src/client/web/WebClient.cpp | 228 +------------ server/src/client/web/WebClient.h | 8 +- server/src/manager/SqlDataManager.cpp | 13 +- server/src/music/MusicBotManager.cpp | 2 +- server/src/rtc/imports.h | 57 ++++ server/src/rtc/lib.cpp | 281 ++++++++++++++++ server/src/rtc/lib.h | 80 +++++ server/src/server/VoiceServer.cpp | 4 +- shared | 2 +- 46 files changed, 913 insertions(+), 966 deletions(-) create mode 100644 .clang-format delete mode 100644 package.json create mode 160000 rtclib delete mode 100644 server/src/client/web/SampleHandler.cpp delete mode 100644 server/src/client/web/SampleHandler.h delete mode 100644 server/src/client/web/VoiceBridge.cpp delete mode 100644 server/src/client/web/VoiceBridge.h create mode 100644 server/src/rtc/imports.h create mode 100644 server/src/rtc/lib.cpp create mode 100644 server/src/rtc/lib.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..341f04d --- /dev/null +++ b/.clang-format @@ -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 diff --git a/.gitmodules b/.gitmodules index 1297527..da63da2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/git-teaspeak b/git-teaspeak index 3133288..d094287 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit 3133288d211f5b85db59a390ae50e42e37cf0f18 +Subproject commit d09428739c651d897ae5b00b394139891050428a diff --git a/package.json b/package.json deleted file mode 100644 index 0a18a55..0000000 --- a/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "TeaSpeak", - "version": "1.0.0", - "dependencies": { - "@types/node": "^14.11.2", - "yaml": "^1.10.0" - } -} diff --git a/rtclib b/rtclib new file mode 160000 index 0000000..69a7427 --- /dev/null +++ b/rtclib @@ -0,0 +1 @@ +Subproject commit 69a7427ed59bd2177671eabf0df999b98b008ae0 diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 5a6fb33..eca0aa4 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -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 diff --git a/server/main.cpp b/server/main.cpp index 402d7ae..3e4b6e5 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -44,9 +44,10 @@ extern void testTomMath(); #define DB_NAME "TeaData.sqlite" #endif -#include -#include #include "src/client/music/internal_provider/channel_replay/ChannelProvider.h" +#include +#include +#include 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]"); diff --git a/server/src/Configuration.cpp b/server/src/Configuration.cpp index 77eac41..fc05fb6 100644 --- a/server/src/Configuration.cpp +++ b/server/src/Configuration.cpp @@ -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> 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> 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> 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, ""); diff --git a/server/src/Configuration.h b/server/src/Configuration.h index 2aa49c5..287f519 100644 --- a/server/src/Configuration.h +++ b/server/src/Configuration.h @@ -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; diff --git a/server/src/TS3ServerClientManager.cpp b/server/src/TS3ServerClientManager.cpp index 364fc0b..4e00e6c 100644 --- a/server/src/TS3ServerClientManager.cpp +++ b/server/src/TS3ServerClientManager.cpp @@ -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(), this); - */ - void VirtualServer::notify_client_ban(const shared_ptr &target, const std::shared_ptr &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(target->currentChannel); s_channel->unregister_client(target); + if(auto client{dynamic_pointer_cast(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(target)}; client) { + this->rtc_server().assign_channel(client->rtc_client_id, s_target_channel->rtc_channel_id); + } + if(auto client{dynamic_pointer_cast(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(target)}; client) { + this->rtc_server().assign_channel(client->rtc_client_id, 0); + } } } TIMING_STEP(timings, "notify view"); diff --git a/server/src/TS3ServerHeartbeat.cpp b/server/src/TS3ServerHeartbeat.cpp index 6cba48e..67b4f52 100644 --- a/server/src/TS3ServerHeartbeat.cpp +++ b/server/src/TS3ServerHeartbeat.cpp @@ -208,7 +208,7 @@ void VirtualServer::executeServerTick() { { BEGIN_TIMINGS(); - this->serverStatistics->tick(); + this->server_statistics_->tick(); { lock_guard 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); } diff --git a/server/src/VirtualServer.cpp b/server/src/VirtualServer.cpp index b0747e5..23be6fe 100644 --- a/server/src/VirtualServer.cpp +++ b/server/src/VirtualServer.cpp @@ -24,6 +24,7 @@ #include "InstanceHandler.h" #include "Configuration.h" #include "VirtualServer.h" +#include "./rtc/lib.h" #include "src/manager/ConversationManager.h" #include #include @@ -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(); + 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(this->ref()); - this->_conversation_manager->initialize(this->_conversation_manager); + this->conversation_manager_ = make_shared(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(serverInstance->getStatistics()); + server_statistics_ = make_shared(serverInstance->getStatistics()); this->serverRoot = std::make_shared(this->sql, self.lock(), this->properties()[property::VIRTUALSERVER_NAME].as(), false); static_pointer_cast(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(self.lock()); - this->musicManager->_self = this->musicManager; - this->musicManager->load_playlists(); - this->musicManager->load_bots(); + this->music_manager_ = make_shared(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); diff --git a/server/src/VirtualServer.h b/server/src/VirtualServer.h index 5e30229..f7c13a4 100644 --- a/server/src/VirtualServer.h +++ b/server/src/VirtualServer.h @@ -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 getServerStatistics(){ return serverStatistics; } + std::shared_ptr getServerStatistics(){ return server_statistics_; } std::shared_ptr getVoiceServer(){ return this->udpVoiceServer; } WebControlServer* getWebServer(){ return this->webControlServer; } @@ -281,7 +286,7 @@ namespace ts { void send_text_message(const std::shared_ptr& /* channel */, const std::shared_ptr& /* sender */, const std::string& /* message */); inline int voice_encryption_mode() { return this->_voice_encryption_mode; } - inline std::shared_ptr conversation_manager() { return this->_conversation_manager; } + inline std::shared_ptr 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 musicManager; - std::shared_ptr serverStatistics; - std::shared_ptr _conversation_manager; + std::shared_ptr music_manager_; + std::shared_ptr server_statistics_; + std::shared_ptr conversation_manager_; + std::unique_ptr rtc_server_; sql::SqlManager* sql; diff --git a/server/src/VirtualServerManager.cpp b/server/src/VirtualServerManager.cpp index 81d192e..eae04c8 100644 --- a/server/src/VirtualServerManager.cpp +++ b/server/src/VirtualServerManager.cpp @@ -361,7 +361,7 @@ shared_ptr 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!"); } diff --git a/server/src/channel/ServerChannel.cpp b/server/src/channel/ServerChannel.cpp index 42f8664..cd68ec3 100644 --- a/server/src/channel/ServerChannel.cpp +++ b/server/src/channel/ServerChannel.cpp @@ -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(this); } @@ -59,18 +58,19 @@ void ServerChannel::setProperties(const std::shared_ptr &ptr) { BasicChannel::setProperties(ptr); } -ServerChannelTree::ServerChannelTree(const std::shared_ptr& server, sql::SqlManager* sql) : sql(sql), server(server) { } +ServerChannelTree::ServerChannelTree(const std::shared_ptr& 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 ServerChannelTree::createChannel(ChannelId parentI std::shared_ptr 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(channel)->setProperties(properties); - static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server.lock(), channel->channelId())); + static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server_ref.lock(), channel->channelId())); channel->properties()[property::CHANNEL_CREATED_AT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); @@ -121,7 +124,7 @@ inline std::shared_ptr 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(parentId, channelId); - auto server = this->server.lock(); + auto server = this->server_ref.lock(); + + std::shared_ptr channel; + if(server) { + auto rtc_channel_id = server->rtc_server().create_channel(); + + channel = std::make_shared(rtc_channel_id, parentId, channelId); + } else { + channel = std::make_shared(0, parentId, channelId); + } static_pointer_cast(channel)->setProperties(serverInstance->databaseHelper()->loadChannelProperties(server, channelId)); static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(server, channel->channelId())); @@ -544,24 +555,30 @@ int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column) } deque ServerChannelTree::deleteChannelRoot(const std::shared_ptr &channel) { - auto server = this->server.lock(); + auto server = this->server_ref.lock(); auto channels = this->delete_channel_root(channel); deque 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 &channel) { BasicChannelTree::on_channel_entry_deleted(channel); - auto server = this->server.lock(); + auto server_channel = dynamic_pointer_cast(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 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 ServerChannelTree::allocateChannel(const shared_ptr &parent, ChannelId channelId) { - return std::make_shared(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(rtc_channel_id, parent->channelId(), channelId); + } else { + return std::make_shared(0, parent->channelId(), channelId); + } } diff --git a/server/src/channel/ServerChannel.h b/server/src/channel/ServerChannel.h index 0d4d42d..d951972 100644 --- a/server/src/channel/ServerChannel.h +++ b/server/src/channel/ServerChannel.h @@ -1,11 +1,11 @@ #pragma once -#include #include #include "Properties.h" #include "PermissionManager.h" #include "BasicChannel.h" #include "../Group.h" +#include "../rtc/lib.h" #include #include @@ -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 &ptr) override; + uint32_t rtc_channel_id; + std::shared_mutex client_lock; std::deque> clients; @@ -37,24 +39,24 @@ namespace ts { class ServerChannelTree : public BasicChannelTree { public: ServerChannelTree(const std::shared_ptr&, sql::SqlManager*); - virtual ~ServerChannelTree(); + ~ServerChannelTree() override; void loadChannelsFromDatabase(); - virtual std::shared_ptr createChannel(ChannelId parentId, ChannelId orderId, const std::string &name) override; + std::shared_ptr createChannel(ChannelId parentId, ChannelId orderId, const std::string &name) override; virtual std::deque deleteChannelRoot(const std::shared_ptr &channel); void deleteSemiPermanentChannels(); std::shared_ptr tree_head() { return this->head; } protected: - virtual ChannelId generateChannelId() override; + ChannelId generateChannelId() override; - virtual void on_channel_entry_deleted(const std::shared_ptr &channel) override; + void on_channel_entry_deleted(const std::shared_ptr &channel) override; std::shared_ptr allocateChannel(const std::shared_ptr &parent, ChannelId channelId) override; private: - std::weak_ptr server; + std::weak_ptr server_ref; ServerId getServerId(); sql::SqlManager* sql; diff --git a/server/src/client/ConnectedClient.h b/server/src/client/ConnectedClient.h index 60ae9ce..d355e01 100644 --- a/server/src/client/ConnectedClient.h +++ b/server/src/client/ConnectedClient.h @@ -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&, bool); virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr&, bool); - virtual bool notifyChannelGroupList(); + virtual bool notifyChannelGroupList(bool as_notify = true); virtual bool notifyConnectionInfo(const std::shared_ptr &target, const std::shared_ptr &info); virtual bool notifyChannelSubscribed(const std::deque> &); virtual bool notifyChannelUnsubscribed(const std::deque> &); diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index a8180ca..4d11d79 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -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) { diff --git a/server/src/client/ConnectedClientTextCommandHandler.cpp b/server/src/client/ConnectedClientTextCommandHandler.cpp index 7726c06..dd15eb3 100644 --- a/server/src/client/ConnectedClientTextCommandHandler.cpp +++ b/server/src/client/ConnectedClientTextCommandHandler.cpp @@ -196,7 +196,7 @@ bool ConnectedClient::handle_text_command( return true; } auto botId = static_cast(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")){ diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index 8985211..eb04525 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -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 &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 &sender) { //if(this->properties()[property::CLIENT_AWAY].as()) return false; if(!this->properties()[property::CLIENT_OUTPUT_HARDWARE].as()) return false; @@ -47,76 +58,14 @@ bool SpeakingClient::shouldReceiveVoiceWhisper(const std::shared_ptrcpmerission_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& client) { - if(client == this) return true; - auto speaking_client = dynamic_pointer_cast(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(); - 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(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(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(this->ref())); + } else if(this->getType() == ClientType::CLIENT_WEB) { + this->rtc_client_id = this->server->rtc_server().create_rtp_client(dynamic_pointer_cast(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()) + if(!this->properties()[property::CLIENT_FLAG_TALKING].as()) { this->properties()[property::CLIENT_FLAG_TALKING] = true; + } + this->speak_time += duration_cast(this->speak_last_packet - this->speak_begin); } else { - if(this->properties()[property::CLIENT_FLAG_TALKING].as()) + if(this->properties()[property::CLIENT_FLAG_TALKING].as()) { 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> 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) 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)}; +} \ No newline at end of file diff --git a/server/src/client/SpeakingClient.h b/server/src/client/SpeakingClient.h index 355207a..927a7e8 100644 --- a/server/src/client/SpeakingClient.h +++ b/server/src/client/SpeakingClient.h @@ -1,7 +1,8 @@ #pragma once -#include #include "ConnectedClient.h" +#include +#include namespace ts::server { class VirtualServer; @@ -31,11 +32,8 @@ namespace ts::server { UNSET = 0xff }; - SpeakingClient(sql::SqlManager* a, const std::shared_ptr& 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& 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 identityData; } handshake; + + rtc::RTCClientId rtc_client_id{0}; }; } \ No newline at end of file diff --git a/server/src/client/command_handler/channel.cpp b/server/src/client/command_handler/channel.cpp index 2cc5dc2..1d88618 100644 --- a/server/src/client/command_handler/channel.cpp +++ b/server/src/client/command_handler/channel.cpp @@ -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(); + 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") { diff --git a/server/src/client/command_handler/misc.cpp b/server/src/client/command_handler/misc.cpp index eb7674e..ee1faee 100644 --- a/server/src/client/command_handler/misc.cpp +++ b/server/src/client/command_handler/misc.cpp @@ -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}; diff --git a/server/src/client/command_handler/music.cpp b/server/src/client/command_handler/music.cpp index 6c57c89..c15379e 100644 --- a/server/src/client/command_handler/music.cpp +++ b/server/src/client/command_handler/music.cpp @@ -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() != 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& 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(); @@ -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(); @@ -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}; { diff --git a/server/src/client/command_handler/server.cpp b/server/src/client/command_handler/server.cpp index 0d8faf7..f7ac9d1 100644 --- a/server/src/client/command_handler/server.cpp +++ b/server/src/client/command_handler/server.cpp @@ -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}; } diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index 9cd8939..52c2ae2 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -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); diff --git a/server/src/client/voice/CryptSetupHandler.cpp b/server/src/client/voice/CryptSetupHandler.cpp index ac80652..549a619 100644 --- a/server/src/client/voice/CryptSetupHandler.cpp +++ b/server/src/client/voice/CryptSetupHandler.cpp @@ -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()); diff --git a/server/src/client/voice/VoiceClient.cpp b/server/src/client/voice/VoiceClient.cpp index ec50c8c..d8add2e 100644 --- a/server/src/client/voice/VoiceClient.cpp +++ b/server/src/client/voice/VoiceClient.cpp @@ -8,8 +8,6 @@ #include #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(); -} \ No newline at end of file +} +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); + } +} diff --git a/server/src/client/voice/VoiceClient.h b/server/src/client/voice/VoiceClient.h index 31e83ec..3cbd157 100644 --- a/server/src/client/voice/VoiceClient.h +++ b/server/src/client/voice/VoiceClient.h @@ -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&); diff --git a/server/src/client/voice/VoiceClientConnection.cpp b/server/src/client/voice/VoiceClientConnection.cpp index d9904b5..ed260cd 100644 --- a/server/src/client/voice/VoiceClientConnection.cpp +++ b/server/src/client/voice/VoiceClientConnection.cpp @@ -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> cb) { this->packet_encoder_.send_command(cmd, b, std::move(cb)); } diff --git a/server/src/client/voice/VoiceClientConnection.h b/server/src/client/voice/VoiceClientConnection.h index e5c2e5b..1fb6915 100644 --- a/server/src/client/voice/VoiceClientConnection.h +++ b/server/src/client/voice/VoiceClientConnection.h @@ -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> /* acknowledge listener */); CryptHandler* getCryptHandler(){ return &crypt_handler; } diff --git a/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp b/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp index 22a0454..3d2f8ea 100644 --- a/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp +++ b/server/src/client/voice/VoiceClientConnectionPacketHandler.cpp @@ -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() + 3, payload.length() - 3}); + } } void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) { diff --git a/server/src/client/web/SampleHandler.cpp b/server/src/client/web/SampleHandler.cpp deleted file mode 100644 index 3148858..0000000 --- a/server/src/client/web/SampleHandler.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#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(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(); - segment->channelCount = this->channelCount; - segment->sampleSize = this->segmentSize; - segment->full = true; - - auto bufLen = this->segmentSize * this->channelCount * sizeof(float); - segment->samples = static_cast(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(); - segment->channelCount = this->channelCount; - segment->sampleSize = overhead; - segment->full = false; - - auto bufLen = overhead * this->channelCount * sizeof(float); - segment->samples = static_cast(malloc(bufLen)); - memcpy(segment->samples, &pcm[pcmIndex], bufLen); - this->segments.push_back(segment); - } -} - -std::shared_ptr 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; -} \ No newline at end of file diff --git a/server/src/client/web/SampleHandler.h b/server/src/client/web/SampleHandler.h deleted file mode 100644 index 1131de3..0000000 --- a/server/src/client/web/SampleHandler.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -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 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> segments; - }; - } -} \ No newline at end of file diff --git a/server/src/client/web/VoiceBridge.cpp b/server/src/client/web/VoiceBridge.cpp deleted file mode 100644 index 3c0321e..0000000 --- a/server/src/client/web/VoiceBridge.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include -#include -#include -#include -#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 loop() { - return std::shared_ptr{(GMainLoop*) g_main_loop_ref(main_loop_), g_main_loop_unref}; - } -} - -VoiceBridge::VoiceBridge(const shared_ptr& owner) : _owner(owner) { - auto config = make_shared(); - config->nice_config = make_shared(); - - 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(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(); - 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(config) ; -} - -VoiceBridge::~VoiceBridge() { - __asm__("nop"); -} - -int VoiceBridge::server_id() { - auto locked = this->_owner.lock(); - return locked ? locked->getServerId() : 0; -} - -std::shared_ptr 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 &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>& 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 &undefined_stream) { - if(undefined_stream->type() == rtc::CHANTYPE_APPLICATION) { - auto stream = dynamic_pointer_cast(undefined_stream); - if(!stream) return; - - stream->callback_datachannel_new = [&](const std::shared_ptr &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(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 &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 &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 &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 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 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 &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 &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); - } -} \ No newline at end of file diff --git a/server/src/client/web/VoiceBridge.h b/server/src/client/web/VoiceBridge.h deleted file mode 100644 index 60de318..0000000 --- a/server/src/client/web/VoiceBridge.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace ts { - namespace server { - class WebClient; - } - namespace web { - class VoiceBridge { - public: - typedef std::function cb_voice_data; - typedef std::function cb_ice_candidate; - typedef std::function cb_ice_candidate_finish; - typedef std::function cb_initialized; - typedef std::function cb_failed; - - std::shared_ptr voice_channel() { return this->voice_channel_; } - std::shared_ptr voice_whisper_channel() { return this->voice_whisper_channel_; } - - explicit VoiceBridge(const std::shared_ptr&); - virtual ~VoiceBridge(); - - bool initialize(std::string&); - bool parse_offer(const std::string&); - int apply_ice(const std::deque>& /* 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& 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 owner(); - - void handle_media_stream(const std::shared_ptr& /* stream */); - void handle_data_channel(const std::shared_ptr & /* channel */); - - void handle_audio_voice_data(const std::shared_ptr& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */); - void handle_audio_voice_whisper_data(const std::shared_ptr& /* 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 _owner; - std::chrono::system_clock::time_point offer_timestamp; - std::unique_ptr connection; - - std::shared_ptr voice_channel_{}; - std::shared_ptr voice_whisper_channel_{}; - - std::weak_ptr incoming_voice_channel_{}; - std::weak_ptr 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 - }; - }; - } -} \ No newline at end of file diff --git a/server/src/client/web/WSWebClient.cpp b/server/src/client/web/WSWebClient.cpp index 45efdf5..e5aac87 100644 --- a/server/src/client/web/WSWebClient.cpp +++ b/server/src/client/web/WSWebClient.cpp @@ -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); } diff --git a/server/src/client/web/WebClient.cpp b/server/src/client/web/WebClient.cpp index e5588f6..1a27831 100644 --- a/server/src/client/web/WebClient.cpp +++ b/server/src/client/web/WebClient.cpp @@ -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(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(dynamic_pointer_cast(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() + 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> candidates; - candidates.push_back(make_shared(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 &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) { diff --git a/server/src/client/web/WebClient.h b/server/src/client/web/WebClient.h index 09a6c60..6910911 100644 --- a/server/src/client/web/WebClient.h +++ b/server/src/client/web/WebClient.h @@ -8,9 +8,7 @@ #include #include #include "misc/queue.h" -#include "SampleHandler.h" #include -#include "VoiceBridge.h" #include #include @@ -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 &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 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; diff --git a/server/src/manager/SqlDataManager.cpp b/server/src/manager/SqlDataManager.cpp index 482a720..788a0de 100644 --- a/server/src/manager/SqlDataManager.cpp +++ b/server/src/manager/SqlDataManager.cpp @@ -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 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; } diff --git a/server/src/music/MusicBotManager.cpp b/server/src/music/MusicBotManager.cpp index 454d103..7a64d1b 100644 --- a/server/src/music/MusicBotManager.cpp +++ b/server/src/music/MusicBotManager.cpp @@ -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 diff --git a/server/src/rtc/imports.h b/server/src/rtc/imports.h new file mode 100644 index 0000000..c6a7938 --- /dev/null +++ b/server/src/rtc/imports.h @@ -0,0 +1,57 @@ +#include +#include + +#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 \ No newline at end of file diff --git a/server/src/rtc/lib.cpp b/server/src/rtc/lib.cpp new file mode 100644 index 0000000..9b4416a --- /dev/null +++ b/server/src/rtc/lib.cpp @@ -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 + +using namespace ts; +using namespace ts::server; +using namespace ts::rtc; + +struct LibCallbackData { + ClientId client_id; + std::weak_ptr 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(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 &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 &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 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(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()); +} \ No newline at end of file diff --git a/server/src/rtc/lib.h b/server/src/rtc/lib.h new file mode 100644 index 0000000..1e46844 --- /dev/null +++ b/server/src/rtc/lib.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include + +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& /* client */); + RTCClientId create_native_client(const std::shared_ptr& /* 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 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; + }; +} \ No newline at end of file diff --git a/server/src/server/VoiceServer.cpp b/server/src/server/VoiceServer.cpp index 3b0e83e..fcb7437 100644 --- a/server/src/server/VoiceServer.cpp +++ b/server/src/server/VoiceServer.cpp @@ -115,7 +115,9 @@ void VoiceServer::triggerWrite(const std::shared_ptr& 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) { diff --git a/shared b/shared index f5c6431..7b51bcc 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit f5c643129e306132437e8b529c2398e9cfc0545a +Subproject commit 7b51bcc5e82802ea8173da8c341ff31614f1c49b