Initial video commit
This commit is contained in:
@@ -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)};
|
||||
}
|
||||
Reference in New Issue
Block a user