diff --git a/native/serverconnection/src/connection/audio/VoiceClient.cpp b/native/serverconnection/src/connection/audio/VoiceClient.cpp index fb30a57..5494a19 100644 --- a/native/serverconnection/src/connection/audio/VoiceClient.cpp +++ b/native/serverconnection/src/connection/audio/VoiceClient.cpp @@ -77,12 +77,12 @@ void VoiceClientWrap::do_wrap(const v8::Local &object) { handle->on_state_changed = [&]{ this->call_state_changed(); }; this->call_state_changed = Nan::async_callback([&]{ - Nan::HandleScope scope; - this->_call_state_changed(); + Nan::HandleScope scope{}; + this->call_state_changed_(); }); } -void VoiceClientWrap::_call_state_changed() { +void VoiceClientWrap::call_state_changed_() { auto handle = this->_handle.lock(); if(!handle) { log_warn(category::voice_connection, tr("State changed on invalid handle!")); @@ -90,23 +90,25 @@ void VoiceClientWrap::_call_state_changed() { } auto state = handle->state(); - auto call_playback_callback = state == VoiceClient::state::playing && !this->_currently_playing; - auto call_stopped_callback = state == VoiceClient::state::stopped && this->_currently_playing; - if(state == VoiceClient::state::stopped) - this->_currently_playing = false; - if(state == VoiceClient::state::playing) - this->_currently_playing = true; - - if(call_playback_callback) { - auto callback = Nan::Get(this->handle(), Nan::New("callback_playback").ToLocalChecked()).ToLocalChecked(); - if(callback->IsFunction()) - callback.As()->Call(Nan::GetCurrentContext(), Nan::Undefined(), 0, nullptr); + const auto was_playing = this->currently_playing_; + if(state == VoiceClient::state::stopped) { + this->currently_playing_ = false; + } else if(state == VoiceClient::state::playing) { + this->currently_playing_ = true; } - if(call_stopped_callback) { + + if(!was_playing && this->currently_playing_) { + auto callback = Nan::Get(this->handle(), Nan::New("callback_playback").ToLocalChecked()).ToLocalChecked(); + if(callback->IsFunction()) { + (void) callback.As()->Call(Nan::GetCurrentContext(), Nan::Undefined(), 0, nullptr); + } + } + if(was_playing && !this->currently_playing_) { auto callback = Nan::Get(this->handle(), Nan::New("callback_stopped").ToLocalChecked()).ToLocalChecked(); - if(callback->IsFunction()) - callback.As()->Call(Nan::GetCurrentContext(), Nan::Undefined(), 0, nullptr); + if(callback->IsFunction()) { + (void) callback.As()->Call(Nan::GetCurrentContext(), Nan::Undefined(), 0, nullptr); + } } auto callback = Nan::Get(this->handle(), Nan::New("callback_state_changed").ToLocalChecked()).ToLocalChecked(); @@ -114,7 +116,7 @@ void VoiceClientWrap::_call_state_changed() { v8::Local argv[1] = { Nan::New(state) }; - callback.As()->Call(Nan::GetCurrentContext(), Nan::Undefined(), 1, argv); + (void) callback.As()->Call(Nan::GetCurrentContext(), Nan::Undefined(), 1, argv); } } @@ -207,16 +209,16 @@ NAN_METHOD(VoiceClientWrap::_get_stream) { VoiceClientWrap::VoiceClientWrap(const std::shared_ptr& client) : _handle(client) { } -VoiceClientWrap::~VoiceClientWrap() {} +VoiceClientWrap::~VoiceClientWrap() = default; VoiceClient::VoiceClient(const std::shared_ptr&, uint16_t client_id) : client_id_(client_id) { this->execute_lock_timeout = std::chrono::microseconds{500}; } VoiceClient::~VoiceClient() { - if(v8::Isolate::GetCurrent()) + if(v8::Isolate::GetCurrent()) { this->finalize_js_object(); - else { + } else { assert(this->js_handle_.IsEmpty()); } @@ -242,28 +244,37 @@ void VoiceClient::initialize() { client->output_source->set_max_buffered_samples((size_t) ceil(client->output_source->sample_rate * 0.5)); client->output_source->set_min_buffered_samples((size_t) ceil(client->output_source->sample_rate * 0.04)); - 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) { - 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()) { - 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); + client->output_source->on_underflow = [weak_this](size_t sample_count){ /* this callback will never be called when the client has been deallocated */ + auto client = weak_this.lock(); + if(!client) { + return false; + } + + if(client->state_ == state::stopping) { + client->set_state(state::stopped); + } else if(client->state_ != state::stopped) { + if(client->_last_received_packet + chrono::seconds{1} < chrono::system_clock::now()) { + client->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->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); - client_ptr->set_state(state::buffering); + if(client->state_ != state::buffering) { + log_warn(category::audio, tr("Client {} has a audio buffer underflow for {} samples. Buffer again."), client->client_id_, sample_count); + client->set_state(state::buffering); } - audio::decode_event_loop->schedule(static_pointer_cast(client_ptr->ref())); + audio::decode_event_loop->schedule(static_pointer_cast(client)); } } 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); + client->output_source->on_overflow = [weak_this](size_t count){ + auto client = weak_this.lock(); + if(!client) { + return; + } + + log_warn(category::audio, tr("Client {} has a audio buffer overflow of {}."), client->client_id_, count); }; }); } @@ -281,7 +292,7 @@ void VoiceClient::initialize_js_object() { auto object_wrap = new VoiceClientWrap(this->ref()); auto object = Nan::NewInstance(Nan::New(VoiceClientWrap::constructor()), 0, nullptr).ToLocalChecked(); - Nan::TryCatch tc; + Nan::TryCatch tc{}; object_wrap->do_wrap(object); if(tc.HasCaught()) { tc.ReThrow(); diff --git a/native/serverconnection/src/connection/audio/VoiceClient.h b/native/serverconnection/src/connection/audio/VoiceClient.h index c6239cf..2a85506 100644 --- a/native/serverconnection/src/connection/audio/VoiceClient.h +++ b/native/serverconnection/src/connection/audio/VoiceClient.h @@ -181,8 +181,8 @@ namespace tc::connection { return my_constructor; } - VoiceClientWrap(const std::shared_ptr&); - virtual ~VoiceClientWrap(); + explicit VoiceClientWrap(const std::shared_ptr&); + ~VoiceClientWrap() override; void do_wrap(const v8::Local&); private: @@ -194,8 +194,8 @@ namespace tc::connection { std::weak_ptr _handle; - bool _currently_playing = false; + bool currently_playing_{false}; Nan::callback_t<> call_state_changed; - void _call_state_changed(); + void call_state_changed_(); }; } \ No newline at end of file