Initial video commit

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