diff --git a/modules/renderer/connection/FileTransfer.ts b/modules/renderer/connection/FileTransfer.ts index 4ef9c9f..239344f 100644 --- a/modules/renderer/connection/FileTransfer.ts +++ b/modules/renderer/connection/FileTransfer.ts @@ -1,7 +1,8 @@ import * as native from "tc-native/connection"; import * as path from "path"; -import {DownloadKey, DownloadTransfer, UploadKey, UploadTransfer} from "tc-shared/FileManager"; +import {DownloadKey, DownloadTransfer, UploadKey, UploadTransfer} from "tc-shared/file/FileManager"; import {base64_encode_ab, str2ab8} from "tc-shared/utils/buffers"; +import {set_transfer_provider, TransferKey, TransferProvider} from "tc-shared/file/FileManager"; class NativeFileDownload implements DownloadTransfer { readonly key: DownloadKey; @@ -169,12 +170,12 @@ class NativeFileUpload implements UploadTransfer { } } +set_transfer_provider(new class implements TransferProvider { + spawn_upload_transfer(key: TransferKey): UploadTransfer { + return new NativeFileUpload(key); + } -export function spawn_download_transfer(key: DownloadKey) : DownloadTransfer { - return new NativeFileDownload(key); -} - - -export function spawn_upload_transfer(key: UploadKey) : UploadTransfer { - return new NativeFileUpload(key); -} \ No newline at end of file + spawn_download_transfer(key: TransferKey): DownloadTransfer { + return new NativeFileDownload(key); + } +}); \ No newline at end of file diff --git a/modules/renderer/index.ts b/modules/renderer/index.ts index c779169..84cd7f3 100644 --- a/modules/renderer/index.ts +++ b/modules/renderer/index.ts @@ -150,6 +150,7 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { require("./context-menu"); require("./app_backend"); require("./icon-helper").initialize(); + require("./connection/FileTransfer"); } catch (error) { console.log(error); window.displayCriticalError("Failed to load native extensions: " + error); diff --git a/native/serverconnection/src/connection/ServerConnection.cpp b/native/serverconnection/src/connection/ServerConnection.cpp index b4464b2..5b17fb4 100644 --- a/native/serverconnection/src/connection/ServerConnection.cpp +++ b/native/serverconnection/src/connection/ServerConnection.cpp @@ -643,6 +643,9 @@ void ServerConnection::close_connection() { void ServerConnection::execute_tick() { if(this->protocol_handler) this->protocol_handler->execute_tick(); + + if(auto vc{this->voice_connection}; vc) + vc->execute_tick(); } void ServerConnection::_execute_callback_commands() { diff --git a/native/serverconnection/src/connection/audio/VoiceClient.cpp b/native/serverconnection/src/connection/audio/VoiceClient.cpp index 08a28d7..e3bbb48 100644 --- a/native/serverconnection/src/connection/audio/VoiceClient.cpp +++ b/native/serverconnection/src/connection/audio/VoiceClient.cpp @@ -208,7 +208,7 @@ VoiceClientWrap::VoiceClientWrap(const std::shared_ptr& client) : _ VoiceClientWrap::~VoiceClientWrap() {} -VoiceClient::VoiceClient(const std::shared_ptr&, uint16_t client_id) : _client_id(client_id) { +VoiceClient::VoiceClient(const std::shared_ptr&, uint16_t client_id) : client_id_(client_id) { this->execute_lock_timeout = std::chrono::microseconds{500}; } @@ -216,7 +216,7 @@ VoiceClient::~VoiceClient() { if(v8::Isolate::GetCurrent()) this->finalize_js_object(); else { - assert(this->_js_handle.IsEmpty()); + assert(this->js_handle_.IsEmpty()); } this->cancel_replay(); /* cleanup all buffers */ @@ -227,7 +227,7 @@ VoiceClient::~VoiceClient() { } void VoiceClient::initialize() { - auto weak_this = this->_ref; + auto weak_this = this->ref_; audio::initialize([weak_this]{ auto client = weak_this.lock(); @@ -241,15 +241,15 @@ void VoiceClient::initialize() { const auto client_ptr = &*client; client->output_source->on_underflow = [client_ptr](size_t sample_count){ /* this callback will never be called when the client has been deallocated */ - if(client_ptr->_state == state::stopping) + if(client_ptr->state_ == state::stopping) client_ptr->set_state(state::stopped); - else if(client_ptr->_state != state::stopped) { - if(client_ptr->_last_received_packet + chrono::seconds(1) < chrono::system_clock::now()) { + else if(client_ptr->state_ != state::stopped) { + if(client_ptr->_last_received_packet + chrono::seconds{1} < chrono::system_clock::now()) { client_ptr->set_state(state::stopped); - log_warn(category::audio, tr("Client {} has a audio buffer underflow for {} samples and not received any data for one second. Stopping replay."), client_ptr->_client_id, sample_count); + log_warn(category::audio, tr("Client {} has a audio buffer underflow for {} samples and not received any data for one second. Stopping replay."), client_ptr->client_id_, sample_count); } else { - if(client_ptr->_state != state::buffering) { - log_warn(category::audio, tr("Client {} has a audio buffer underflow for {} samples. Buffer again."), client_ptr->_client_id, sample_count); + if(client_ptr->state_ != state::buffering) { + log_warn(category::audio, tr("Client {} has a audio buffer underflow for {} samples. Buffer again."), client_ptr->client_id_, sample_count); client_ptr->set_state(state::buffering); } @@ -260,13 +260,20 @@ void VoiceClient::initialize() { return false; }; client->output_source->on_overflow = [client_ptr](size_t count){ - log_warn(category::audio, tr("Client {} has a audio buffer overflow of {}."), client_ptr->_client_id, count); + log_warn(category::audio, tr("Client {} has a audio buffer overflow of {}."), client_ptr->client_id_, count); }; }); } +void VoiceClient::execute_tick() { + if(this->state_ == state::buffering && this->_last_received_packet + chrono::milliseconds{250} < chrono::system_clock::now()) { + this->set_state(state::stopped); + log_debug(category::audio, tr("Audio stop packet for client {} seems to be lost. Stopping playback."), this->client_id_); + } +} + void VoiceClient::initialize_js_object() { - if(!this->_js_handle.IsEmpty()) + if(!this->js_handle_.IsEmpty()) return; auto object_wrap = new VoiceClientWrap(this->ref()); @@ -278,11 +285,11 @@ void VoiceClient::initialize_js_object() { return; } - this->_js_handle.Reset(Nan::GetCurrentContext()->GetIsolate(), object); + this->js_handle_.Reset(Nan::GetCurrentContext()->GetIsolate(), object); } void VoiceClient::finalize_js_object() { - this->_js_handle.Reset(); + this->js_handle_.Reset(); } /** @@ -328,7 +335,7 @@ void VoiceClient::process_packet(uint16_t packet_id, const pipes::buffer_view& b } #endif if(codec < 0 || codec > this->codec.size()) { - log_warn(category::voice_connection, tr("Received voice packet from client {} with unknown codec ({})"), this->_client_id, codec); + log_warn(category::voice_connection, tr("Received voice packet from client {} with unknown codec ({})"), this->client_id_, codec); return; } @@ -396,7 +403,7 @@ void VoiceClient::process_packet(uint16_t packet_id, const pipes::buffer_view& b } void VoiceClient::cancel_replay() { - log_trace(category::voice_connection, tr("Cancel replay for client {}"), this->_client_id); + log_trace(category::voice_connection, tr("Cancel replay for client {}"), this->client_id_); if(output_source) this->output_source->clear(); this->set_state(state::stopped); @@ -547,14 +554,14 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch while(replay_head) { if(replay_head->buffer.empty()) { this->set_state(state::stopping); - log_debug(category::voice_connection, tr("Client {} send a stop signal. Flushing stream and stopping"), this->_client_id); + log_debug(category::voice_connection, tr("Client {} send a stop signal. Flushing stream and stopping"), this->client_id_); } else { auto lost_packets = packet_id_diff(local_last_pid, replay_head->packet_id) - 1; if(lost_packets > 10) { - log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream ({} to {}). Resetting decoder."), this->_client_id, lost_packets, local_last_pid, replay_head->packet_id); + log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream ({} to {}). Resetting decoder."), this->client_id_, lost_packets, local_last_pid, replay_head->packet_id); replay_head->reset_decoder = true; } else if(lost_packets > 0) { - log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream. FEC decoding it."), this->_client_id, lost_packets); + log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream. FEC decoding it."), this->client_id_, lost_packets); /* if(audio_codec.converter->decode_lost(error, lost_packets)) log_warn(category::audio, tr("Failed to decode lost packets for client {}: {}"), this->_client_id, error); @@ -564,7 +571,7 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch this->output_source->enqueue_samples(decoded->sample_data, decoded->sample_size); } - const auto is_new_audio_stream = this->_state != state::buffering && this->_state != state::playing; + const auto is_new_audio_stream = this->state_ != state::buffering && this->state_ != state::playing; if(replay_head->reset_decoder || is_new_audio_stream) { audio_codec.converter->reset_decoder(); replay_head->reset_decoder = false; @@ -595,10 +602,10 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch //TODO: Use statically allocated buffer? auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer, false); if(!decoded) { - log_warn(category::audio, tr("Failed to decode buffer for client {} (nullptr). Dropping buffer."), this->_client_id, error); + log_warn(category::audio, tr("Failed to decode buffer for client {} (nullptr). Dropping buffer."), this->client_id_, error); } else { if(is_new_audio_stream) { - log_warn(category::audio, tr("New audio chunk for client {}"), this->_client_id); + log_warn(category::audio, tr("New audio chunk for client {}"), this->client_id_); //this->output_source->enqueue_silence((size_t) ceil(0.0075f * (float) this->output_source->sample_rate)); /* enqueue 7.5ms silence so we give the next packet a chance to be send */ } @@ -640,7 +647,7 @@ void VoiceClient::initialize_code(const codec::value &audio_codec) { auto info = codec::get_info(audio_codec); if(!info || !info->supported) { - log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Codec is not supported"), audio_codec, this->_client_id); + log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Codec is not supported"), audio_codec, this->client_id_); codec_data.state = AudioCodec::State::UNSUPPORTED; return; } @@ -648,18 +655,18 @@ void VoiceClient::initialize_code(const codec::value &audio_codec) { codec_data.state = AudioCodec::State::INITIALIZED_FAIL; codec_data.converter = info->new_converter(error); if(!codec_data.converter) { - log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Failed to initialize decoder: {}"), audio_codec, this->_client_id, error); + log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Failed to initialize decoder: {}"), audio_codec, this->client_id_, error); return; } codec_data.resampler = make_shared(codec_data.converter->sample_rate(), this->output_source->sample_rate, this->output_source->channel_count); if(!codec_data.resampler->valid()) { - log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Failed to initialize resampler"), audio_codec, this->_client_id); + log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Failed to initialize resampler"), audio_codec, this->client_id_); return; } codec_data.state = AudioCodec::State::INITIALIZED_SUCCESSFULLY; - log_trace(category::voice_connection, tr("Successfully initialized codec {} for client {}."), audio_codec, this->_client_id); + log_trace(category::voice_connection, tr("Successfully initialized codec {} for client {}."), audio_codec, this->client_id_); } std::shared_ptr VoiceClient::decode_buffer(const codec::value &audio_codec, const pipes::buffer_view &buffer, bool fec) { @@ -699,11 +706,11 @@ std::shared_ptr VoiceClient::decode_buffer(const codec::val return nullptr; } - if(this->_volume != 1) { + if(this->volume_ != 1) { auto buf = (float*) target_buffer; auto count = this->output_source->channel_count * resampled_samples; while(count-- > 0) - *(buf++) *= this->_volume; + *(buf++) *= this->volume_; } auto audio_buffer = audio::SampleBuffer::allocate((uint8_t) this->output_source->channel_count, (uint16_t) resampled_samples); diff --git a/native/serverconnection/src/connection/audio/VoiceClient.h b/native/serverconnection/src/connection/audio/VoiceClient.h index bec7f17..698a7b8 100644 --- a/native/serverconnection/src/connection/audio/VoiceClient.h +++ b/native/serverconnection/src/connection/audio/VoiceClient.h @@ -69,24 +69,25 @@ namespace tc::connection { void initialize(); - inline uint16_t client_id() { return this->_client_id; } + inline uint16_t client_id() { return this->client_id_; } void initialize_js_object(); void finalize_js_object(); v8::Local js_handle() { assert(v8::Isolate::GetCurrent()); - return this->_js_handle.Get(Nan::GetCurrentContext()->GetIsolate()); + return this->js_handle_.Get(Nan::GetCurrentContext()->GetIsolate()); } - inline std::shared_ptr ref() { return this->_ref.lock(); } + inline std::shared_ptr ref() { return this->ref_.lock(); } void process_packet(uint16_t packet_id, const pipes::buffer_view& /* buffer */, codec::value /* codec */, bool /* head */); + void execute_tick(); - inline float get_volume() { return this->_volume; } - inline void set_volume(float value) { this->_volume = value; } + inline float get_volume() { return this->volume_; } + inline void set_volume(float value) { this->volume_ = value; } - inline state::value state() { return this->_state; } + inline state::value state() { return this->state_; } void cancel_replay(); @@ -143,18 +144,18 @@ namespace tc::connection { /* might be null (if audio hasn't been initialized) */ std::shared_ptr output_source; - std::weak_ptr _ref; - v8::Persistent _js_handle; + std::weak_ptr ref_; + v8::Persistent js_handle_; - uint16_t _client_id{0}; - float _volume = 1.f; + uint16_t client_id_{0}; + float volume_{1.f}; std::chrono::system_clock::time_point _last_received_packet; - state::value _state = state::stopped; + state::value state_ = state::stopped; inline void set_state(state::value value) { - if(value == this->_state) + if(value == this->state_) return; - this->_state = value; + this->state_ = value; if(this->on_state_changed) this->on_state_changed(); } diff --git a/native/serverconnection/src/connection/audio/VoiceConnection.cpp b/native/serverconnection/src/connection/audio/VoiceConnection.cpp index 7fb1565..a853b46 100644 --- a/native/serverconnection/src/connection/audio/VoiceConnection.cpp +++ b/native/serverconnection/src/connection/audio/VoiceConnection.cpp @@ -296,10 +296,20 @@ VoiceConnection::~VoiceConnection() { } void VoiceConnection::reset() { - lock_guard lock(this->_clients_lock); + lock_guard lock{this->_clients_lock}; this->_clients.clear(); } +void VoiceConnection::execute_tick() { + decltype(this->_clients) clients{}; + { + std::lock_guard lg{this->_clients_lock}; + clients = this->_clients; + } + for(auto& client : clients) + client->execute_tick(); +} + void VoiceConnection::initialize_js_object() { auto object_wrap = new VoiceConnectionWrap(this->ref()); auto object = Nan::NewInstance(Nan::New(VoiceConnectionWrap::constructor()), 0, nullptr).ToLocalChecked(); @@ -313,7 +323,7 @@ void VoiceConnection::finalize_js_object() { } std::shared_ptr VoiceConnection::find_client(uint16_t client_id) { - lock_guard lock(this->_clients_lock); + lock_guard lock{this->_clients_lock}; for(const auto& client : this->_clients) if(client->client_id() == client_id) return client; @@ -322,12 +332,12 @@ std::shared_ptr VoiceConnection::find_client(uint16_t client_id) { } std::shared_ptr VoiceConnection::register_client(uint16_t client_id) { - lock_guard lock(this->_clients_lock); + lock_guard lock{this->_clients_lock}; auto client = this->find_client(client_id); if(client) return client; client = make_shared(this->ref(), client_id); - client->_ref = client; + client->ref_ = client; client->initialize(); this->_clients.push_back(client); return client; diff --git a/native/serverconnection/src/connection/audio/VoiceConnection.h b/native/serverconnection/src/connection/audio/VoiceConnection.h index 9347dee..3d0e216 100644 --- a/native/serverconnection/src/connection/audio/VoiceConnection.h +++ b/native/serverconnection/src/connection/audio/VoiceConnection.h @@ -68,6 +68,7 @@ namespace tc { virtual ~VoiceConnection(); void reset(); + void execute_tick(); void initialize_js_object(); void finalize_js_object(); diff --git a/package.json b/package.json index 3426813..6566c9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "TeaClient", - "version": "1.4.6-1", + "version": "1.4.6-2", "description": "", "main": "main.js", "scripts": { @@ -13,7 +13,7 @@ "start-n": "electron . -t --disable-hardware-acceleration --no-single-instance -u=https://clientapi.teaspeak.de/ -d", "start-nd": "electron . -t --disable-hardware-acceleration --no-single-instance -u=http://clientapi.teaspeak.dev/ -d", "start-01": "electron . --updater-channel=test -u=http://dev.clientapi.teaspeak.de/ -d --updater-ui-loader_type=0 --updater-local-version=1.0.1", - "start-s": "electron . --disable-hardware-acceleration --debug --updater-ui-loader_type=2 --updater-ui-ignore-version -t -u http://localhost:8081/", + "start-s": "electron . --disable-hardware-acceleration --gdb --debug --updater-ui-loader_type=2 --updater-ui-ignore-version -t -u http://localhost:8081/", "dtest": "electron . dtest", "compile-sass": "sass --update .:.", "compile-tsc": "tsc",