Some minor bugfixes

This commit is contained in:
WolverinDEV 2020-05-04 11:54:35 +02:00
parent 07958ec36b
commit 438b2ad1c6
8 changed files with 79 additions and 55 deletions

View File

@ -1,7 +1,8 @@
import * as native from "tc-native/connection"; import * as native from "tc-native/connection";
import * as path from "path"; 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 {base64_encode_ab, str2ab8} from "tc-shared/utils/buffers";
import {set_transfer_provider, TransferKey, TransferProvider} from "tc-shared/file/FileManager";
class NativeFileDownload implements DownloadTransfer { class NativeFileDownload implements DownloadTransfer {
readonly key: DownloadKey; readonly key: DownloadKey;
@ -169,12 +170,12 @@ class NativeFileUpload implements UploadTransfer {
} }
} }
set_transfer_provider(new class implements TransferProvider {
export function spawn_download_transfer(key: DownloadKey) : DownloadTransfer { spawn_upload_transfer(key: TransferKey): UploadTransfer {
return new NativeFileDownload(key);
}
export function spawn_upload_transfer(key: UploadKey) : UploadTransfer {
return new NativeFileUpload(key); return new NativeFileUpload(key);
} }
spawn_download_transfer(key: TransferKey): DownloadTransfer {
return new NativeFileDownload(key);
}
});

View File

@ -150,6 +150,7 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
require("./context-menu"); require("./context-menu");
require("./app_backend"); require("./app_backend");
require("./icon-helper").initialize(); require("./icon-helper").initialize();
require("./connection/FileTransfer");
} catch (error) { } catch (error) {
console.log(error); console.log(error);
window.displayCriticalError("Failed to load native extensions: " + error); window.displayCriticalError("Failed to load native extensions: " + error);

View File

@ -643,6 +643,9 @@ void ServerConnection::close_connection() {
void ServerConnection::execute_tick() { void ServerConnection::execute_tick() {
if(this->protocol_handler) if(this->protocol_handler)
this->protocol_handler->execute_tick(); this->protocol_handler->execute_tick();
if(auto vc{this->voice_connection}; vc)
vc->execute_tick();
} }
void ServerConnection::_execute_callback_commands() { void ServerConnection::_execute_callback_commands() {

View File

@ -208,7 +208,7 @@ VoiceClientWrap::VoiceClientWrap(const std::shared_ptr<VoiceClient>& client) : _
VoiceClientWrap::~VoiceClientWrap() {} VoiceClientWrap::~VoiceClientWrap() {}
VoiceClient::VoiceClient(const std::shared_ptr<VoiceConnection>&, uint16_t client_id) : _client_id(client_id) { VoiceClient::VoiceClient(const std::shared_ptr<VoiceConnection>&, uint16_t client_id) : client_id_(client_id) {
this->execute_lock_timeout = std::chrono::microseconds{500}; this->execute_lock_timeout = std::chrono::microseconds{500};
} }
@ -216,7 +216,7 @@ VoiceClient::~VoiceClient() {
if(v8::Isolate::GetCurrent()) if(v8::Isolate::GetCurrent())
this->finalize_js_object(); this->finalize_js_object();
else { else {
assert(this->_js_handle.IsEmpty()); assert(this->js_handle_.IsEmpty());
} }
this->cancel_replay(); /* cleanup all buffers */ this->cancel_replay(); /* cleanup all buffers */
@ -227,7 +227,7 @@ VoiceClient::~VoiceClient() {
} }
void VoiceClient::initialize() { void VoiceClient::initialize() {
auto weak_this = this->_ref; auto weak_this = this->ref_;
audio::initialize([weak_this]{ audio::initialize([weak_this]{
auto client = weak_this.lock(); auto client = weak_this.lock();
@ -241,15 +241,15 @@ void VoiceClient::initialize() {
const auto client_ptr = &*client; 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 */ 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); client_ptr->set_state(state::stopped);
else if(client_ptr->_state != state::stopped) { else if(client_ptr->state_ != state::stopped) {
if(client_ptr->_last_received_packet + chrono::seconds(1) < chrono::system_clock::now()) { if(client_ptr->_last_received_packet + chrono::seconds{1} < chrono::system_clock::now()) {
client_ptr->set_state(state::stopped); 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 { } else {
if(client_ptr->_state != state::buffering) { 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); 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); client_ptr->set_state(state::buffering);
} }
@ -260,13 +260,20 @@ void VoiceClient::initialize() {
return false; return false;
}; };
client->output_source->on_overflow = [client_ptr](size_t count){ 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() { void VoiceClient::initialize_js_object() {
if(!this->_js_handle.IsEmpty()) if(!this->js_handle_.IsEmpty())
return; return;
auto object_wrap = new VoiceClientWrap(this->ref()); auto object_wrap = new VoiceClientWrap(this->ref());
@ -278,11 +285,11 @@ void VoiceClient::initialize_js_object() {
return; return;
} }
this->_js_handle.Reset(Nan::GetCurrentContext()->GetIsolate(), object); this->js_handle_.Reset(Nan::GetCurrentContext()->GetIsolate(), object);
} }
void VoiceClient::finalize_js_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 #endif
if(codec < 0 || codec > this->codec.size()) { 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; return;
} }
@ -396,7 +403,7 @@ void VoiceClient::process_packet(uint16_t packet_id, const pipes::buffer_view& b
} }
void VoiceClient::cancel_replay() { 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(); if(output_source) this->output_source->clear();
this->set_state(state::stopped); this->set_state(state::stopped);
@ -547,14 +554,14 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
while(replay_head) { while(replay_head) {
if(replay_head->buffer.empty()) { if(replay_head->buffer.empty()) {
this->set_state(state::stopping); 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 { } else {
auto lost_packets = packet_id_diff(local_last_pid, replay_head->packet_id) - 1; auto lost_packets = packet_id_diff(local_last_pid, replay_head->packet_id) - 1;
if(lost_packets > 10) { 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; replay_head->reset_decoder = true;
} else if(lost_packets > 0) { } 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)) 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); 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); 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) { if(replay_head->reset_decoder || is_new_audio_stream) {
audio_codec.converter->reset_decoder(); audio_codec.converter->reset_decoder();
replay_head->reset_decoder = false; 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? //TODO: Use statically allocated buffer?
auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer, false); auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer, false);
if(!decoded) { 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 { } else {
if(is_new_audio_stream) { 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 */ //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); auto info = codec::get_info(audio_codec);
if(!info || !info->supported) { 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; codec_data.state = AudioCodec::State::UNSUPPORTED;
return; return;
} }
@ -648,18 +655,18 @@ void VoiceClient::initialize_code(const codec::value &audio_codec) {
codec_data.state = AudioCodec::State::INITIALIZED_FAIL; codec_data.state = AudioCodec::State::INITIALIZED_FAIL;
codec_data.converter = info->new_converter(error); codec_data.converter = info->new_converter(error);
if(!codec_data.converter) { 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; return;
} }
codec_data.resampler = make_shared<audio::AudioResampler>(codec_data.converter->sample_rate(), this->output_source->sample_rate, this->output_source->channel_count); codec_data.resampler = make_shared<audio::AudioResampler>(codec_data.converter->sample_rate(), this->output_source->sample_rate, this->output_source->channel_count);
if(!codec_data.resampler->valid()) { 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; return;
} }
codec_data.state = AudioCodec::State::INITIALIZED_SUCCESSFULLY; 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<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::value &audio_codec, const pipes::buffer_view &buffer, bool fec) { std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::value &audio_codec, const pipes::buffer_view &buffer, bool fec) {
@ -699,11 +706,11 @@ std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::val
return nullptr; return nullptr;
} }
if(this->_volume != 1) { if(this->volume_ != 1) {
auto buf = (float*) target_buffer; auto buf = (float*) target_buffer;
auto count = this->output_source->channel_count * resampled_samples; auto count = this->output_source->channel_count * resampled_samples;
while(count-- > 0) 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); auto audio_buffer = audio::SampleBuffer::allocate((uint8_t) this->output_source->channel_count, (uint16_t) resampled_samples);

View File

@ -69,24 +69,25 @@ namespace tc::connection {
void initialize(); 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 initialize_js_object();
void finalize_js_object(); void finalize_js_object();
v8::Local<v8::Object> js_handle() { v8::Local<v8::Object> js_handle() {
assert(v8::Isolate::GetCurrent()); assert(v8::Isolate::GetCurrent());
return this->_js_handle.Get(Nan::GetCurrentContext()->GetIsolate()); return this->js_handle_.Get(Nan::GetCurrentContext()->GetIsolate());
} }
inline std::shared_ptr<VoiceClient> ref() { return this->_ref.lock(); } inline std::shared_ptr<VoiceClient> ref() { return this->ref_.lock(); }
void process_packet(uint16_t packet_id, const pipes::buffer_view& /* buffer */, codec::value /* codec */, bool /* head */); 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 float get_volume() { return this->volume_; }
inline void set_volume(float value) { this->_volume = value; } 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(); void cancel_replay();
@ -143,18 +144,18 @@ namespace tc::connection {
/* might be null (if audio hasn't been initialized) */ /* might be null (if audio hasn't been initialized) */
std::shared_ptr<audio::AudioOutputSource> output_source; std::shared_ptr<audio::AudioOutputSource> output_source;
std::weak_ptr<VoiceClient> _ref; std::weak_ptr<VoiceClient> ref_;
v8::Persistent<v8::Object> _js_handle; v8::Persistent<v8::Object> js_handle_;
uint16_t _client_id{0}; uint16_t client_id_{0};
float _volume = 1.f; float volume_{1.f};
std::chrono::system_clock::time_point _last_received_packet; 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) { inline void set_state(state::value value) {
if(value == this->_state) if(value == this->state_)
return; return;
this->_state = value; this->state_ = value;
if(this->on_state_changed) if(this->on_state_changed)
this->on_state_changed(); this->on_state_changed();
} }

View File

@ -296,10 +296,20 @@ VoiceConnection::~VoiceConnection() {
} }
void VoiceConnection::reset() { void VoiceConnection::reset() {
lock_guard lock(this->_clients_lock); lock_guard lock{this->_clients_lock};
this->_clients.clear(); 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() { void VoiceConnection::initialize_js_object() {
auto object_wrap = new VoiceConnectionWrap(this->ref()); auto object_wrap = new VoiceConnectionWrap(this->ref());
auto object = Nan::NewInstance(Nan::New(VoiceConnectionWrap::constructor()), 0, nullptr).ToLocalChecked(); auto object = Nan::NewInstance(Nan::New(VoiceConnectionWrap::constructor()), 0, nullptr).ToLocalChecked();
@ -313,7 +323,7 @@ void VoiceConnection::finalize_js_object() {
} }
std::shared_ptr<VoiceClient> VoiceConnection::find_client(uint16_t client_id) { std::shared_ptr<VoiceClient> 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) for(const auto& client : this->_clients)
if(client->client_id() == client_id) if(client->client_id() == client_id)
return client; return client;
@ -322,12 +332,12 @@ std::shared_ptr<VoiceClient> VoiceConnection::find_client(uint16_t client_id) {
} }
std::shared_ptr<VoiceClient> VoiceConnection::register_client(uint16_t client_id) { std::shared_ptr<VoiceClient> 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); auto client = this->find_client(client_id);
if(client) return client; if(client) return client;
client = make_shared<VoiceClient>(this->ref(), client_id); client = make_shared<VoiceClient>(this->ref(), client_id);
client->_ref = client; client->ref_ = client;
client->initialize(); client->initialize();
this->_clients.push_back(client); this->_clients.push_back(client);
return client; return client;

View File

@ -68,6 +68,7 @@ namespace tc {
virtual ~VoiceConnection(); virtual ~VoiceConnection();
void reset(); void reset();
void execute_tick();
void initialize_js_object(); void initialize_js_object();
void finalize_js_object(); void finalize_js_object();

View File

@ -1,6 +1,6 @@
{ {
"name": "TeaClient", "name": "TeaClient",
"version": "1.4.6-1", "version": "1.4.6-2",
"description": "", "description": "",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
@ -13,7 +13,7 @@
"start-n": "electron . -t --disable-hardware-acceleration --no-single-instance -u=https://clientapi.teaspeak.de/ -d", "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-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-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", "dtest": "electron . dtest",
"compile-sass": "sass --update .:.", "compile-sass": "sass --update .:.",
"compile-tsc": "tsc", "compile-tsc": "tsc",