Some changes

This commit is contained in:
WolverinDEV 2019-09-22 14:14:02 +02:00
parent 9e5371fa88
commit 4925e33802
11 changed files with 217 additions and 71 deletions

2
github

@ -1 +1 @@
Subproject commit 06391c6cdd772c2f83c1387960f7224f7cd9f514
Subproject commit 2dd1f60e8d034a68bbca9afe294070c8a83caa9e

View File

@ -61,11 +61,30 @@ function spawn_main_window(entry_point: string) {
});
});
main_window.webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures) => {
console.log("Got new window " + frameName);
const url_preview = require("./url-preview");
url_preview.open_preview(url);
main_window.webContents.on('new-window', (event, url_str, frameName, disposition, options, additionalFeatures) => {
event.preventDefault();
try {
let url: URL;
try {
url = new URL(url_str);
} catch(error) {
throw "failed to parse URL";
}
{
let protocol = url.protocol.endsWith(":") ? url.protocol.substring(0, url.protocol.length - 1) : url.protocol;
if(protocol !== "https" && protocol !== "http") {
throw "invalid protocol (" + protocol + "). HTTP(S) are only supported!";
}
}
console.log("Got new window " + frameName);
const url_preview = require("./url-preview");
url_preview.open_preview(url_str);
} catch(error) {
console.error("Failed to open preview window for URL %s: %o", url_str, error);
dialog.showErrorBox("Failed to open preview", "Failed to open preview URL: " + url_str + "\nError: " + error);
}
});
main_window.webContents.on('crashed', event => {

View File

@ -77,6 +77,7 @@ export async function open_preview(url: string) {
}
}
console.log("Opening URL '%s' as preview.", url);
global_window.webContents.send('preview', url);
if(!global_window.isFocused())
global_window.focus();

View File

@ -135,8 +135,18 @@ void EventExecutor::_executor(tc::event::EventExecutor *loop) {
event_handler->_event_ptr = nullptr;
lock.unlock();
if(event_handler)
event_handler->event_execute(linked_entry->scheduled);
if(event_handler) {
if(event_handler->single_thread_executed()) {
auto execute_lock = event_handler->execute_lock(false);
if(!execute_lock) {
event_handler->event_execute_dropped(linked_entry->scheduled);
} else {
event_handler->event_execute(linked_entry->scheduled);
}
} else {
event_handler->event_execute(linked_entry->scheduled);
}
}
delete linked_entry;
}
}

View File

@ -14,9 +14,21 @@ namespace tc {
friend class EventExecutor;
public:
virtual void event_execute(const std::chrono::system_clock::time_point& /* scheduled timestamp */) = 0;
virtual void event_execute_dropped(const std::chrono::system_clock::time_point& /* scheduled timestamp */) {}
std::unique_lock<std::mutex> execute_lock(bool force) {
if(force)
return std::unique_lock<std::mutex>(this->_execute_mutex);
else
return std::unique_lock<std::mutex>(this->_execute_mutex, std::try_to_lock);
}
inline bool single_thread_executed() const { return this->_single_thread; }
inline void single_thread_executed(bool value) { this->_single_thread = value; }
private:
void* _event_ptr = nullptr;
bool _single_thread = true; /* if its set to true there might are some dropped executes! */
std::mutex _execute_mutex;
};
class EventExecutor {

View File

@ -28,7 +28,7 @@ ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
memcpy(buffer, (char *) buf->sample_data + this->channel_count * buf->sample_index * 4, sc * this->channel_count * 4);
} else {
#ifndef WIN32
/* fot my debugger */
/* for my debugger */
__asm__("nop");
#endif
}

View File

@ -23,6 +23,10 @@
#include "audio/js/AudioFilter.h"
#include "connection/audio/AudioEventLoop.h"
#ifndef WIN32
#include <unistd.h>
#endif
extern "C" {
#include <tomcrypt_misc.h>
#include <tomcrypt.h>
@ -69,7 +73,11 @@ tc::audio::AudioOutput* global_audio_output;
NAN_MODULE_INIT(init) {
logger::initialize_node();
logger::info(category::general, "Hello World from C");
#ifndef WIN32
logger::info(category::general, tr("Hello World from C. PPID: {}, PID: {}"), getppid(), getpid());
#else
logger::info(category::general, tr("Hello World from C."), getppid(), getpid());
#endif
/*
{

View File

@ -4,6 +4,7 @@
#include "audio/VoiceConnection.h"
#include "audio/AudioSender.h"
#include "../logger.h"
#include "../hwuid.h"
#include <sstream>
#include <thread>
@ -11,8 +12,12 @@
#include <misc/net.h>
#include <misc/base64.h>
#include <misc/endianness.h>
#include <misc/strobf.h>
#include <iomanip>
//#define FUZZ_VOICE
//#define SHUFFLE_VOICE
using namespace std;
using namespace std::chrono;
using namespace tc::connection;
@ -195,6 +200,10 @@ void ServerConnection::schedule_resend(const std::chrono::system_clock::time_poi
}
}
NAN_METHOD(ServerConnection::connect) {
if(!this->protocol_handler) {
Nan::ThrowError("ServerConnection not initialized");
return;
}
if(info.Length() != 1) {
Nan::ThrowError(tr("Invalid argument count"));
return;
@ -231,8 +240,8 @@ NAN_METHOD(ServerConnection::connect) {
return;
}
unique_lock disconnect_lock(this->disconnect_lock, defer_lock);
if(!disconnect_lock.try_lock_for(chrono::milliseconds(500))) {
unique_lock _disconnect_lock(this->disconnect_lock, defer_lock);
if(!_disconnect_lock.try_lock_for(chrono::milliseconds(500))) {
Nan::ThrowError(tr("failed to acquire disconnect lock"));
return;
}
@ -294,7 +303,6 @@ NAN_METHOD(ServerConnection::connect) {
}
this->socket->on_data = [&](const pipes::buffer_view& buffer) { this->protocol_handler->progress_packet(buffer); };
if(teamspeak->IsBoolean() && teamspeak->BooleanValue(info.GetIsolate()))
this->protocol_handler->server_type = server_type::TEAMSPEAK;
this->protocol_handler->connect();
@ -333,7 +341,9 @@ NAN_METHOD(ServerConnection::disconnect) {
return;
}
this->protocol_handler->disconnect(*Nan::Utf8String(info[0]));
if(this->protocol_handler) {
this->protocol_handler->disconnect(*Nan::Utf8String(info[0]));
}
}
NAN_METHOD(ServerConnection::_error_message) {
@ -365,6 +375,11 @@ NAN_METHOD(ServerConnection::_send_command) {
return ObjectWrap::Unwrap<ServerConnection>(info.Holder())->send_command(info);
}
NAN_METHOD(ServerConnection::send_command) {
if(!this->protocol_handler) {
Nan::ThrowError("ServerConnection not initialized");
return;
}
if(info.Length() != 3) {
Nan::ThrowError("invalid argument count");
return;
@ -432,6 +447,7 @@ NAN_METHOD(ServerConnection::send_command) {
cmd["client_version"] = "0.0.1 [Build: 1549713549]";
cmd["client_platform"] = "Linux";
cmd["client_version_sign"] = "7XvKmrk7uid2ixHFeERGqcC8vupeQqDypLtw2lY9slDNPojEv//F47UaDLG+TmVk4r6S0TseIKefzBpiRtLDAQ==";
cmd[strobf("hwid").string()] = system_uuid(); /* we dont want anybody to patch this out */
}
}
this->protocol_handler->send_command(cmd);
@ -442,6 +458,11 @@ NAN_METHOD(ServerConnection::_send_voice_data) {
}
NAN_METHOD(ServerConnection::send_voice_data) {
if(!this->protocol_handler) {
Nan::ThrowError("ServerConnection not initialized");
return;
}
if(info.Length() != 3) {
Nan::ThrowError("invalid argument count");
return;
@ -480,11 +501,13 @@ NAN_METHOD(ServerConnection::send_voice_data_raw) {
auto flag_head = info[2]->BooleanValue(info.GetIsolate());
auto voice_data = info[0].As<v8::Float32Array>()->Buffer();
auto vs = this->voice_connection->voice_sender();
vs->send_data(voice_data->GetContents().Data(), voice_data->GetContents().ByteLength() / (4 * channels), sample_rate, channels);
auto vs = this->voice_connection ? this->voice_connection->voice_sender() : nullptr;
if(vs) vs->send_data(voice_data->GetContents().Data(), voice_data->GetContents().ByteLength() / (4 * channels), sample_rate, channels);
}
#ifdef SHUFFLE_VOICE
static shared_ptr<ts::protocol::ClientPacket> shuffle_cached_packet;
#endif
void ServerConnection::send_voice_data(const void *buffer, size_t buffer_length, uint8_t codec, bool head) {
auto _buffer = pipes::buffer{ts::protocol::ClientPacket::META_SIZE + buffer_length + 3};
auto packet = make_shared<ts::protocol::ClientPacket>(_buffer);
@ -501,13 +524,19 @@ void ServerConnection::send_voice_data(const void *buffer, size_t buffer_length,
packet->enable_flag(ts::protocol::PacketFlag::Compressed);
packet->enable_flag(ts::protocol::PacketFlag::Unencrypted);
//#define FUZZ_VOICE
#ifdef FUZZ_VOICE
if((rand() % 10) < 2) {
log_info(category::connection, tr("Dropping voice packet"));
} else {
this->protocol_handler->send_packet(packet);
}
#elif defined(SHUFFLE_VOICE)
if(shuffle_cached_packet) {
this->protocol_handler->send_packet(packet);
this->protocol_handler->send_packet(std::exchange(shuffle_cached_packet, nullptr));
} else {
shuffle_cached_packet = packet;
}
#else
this->protocol_handler->send_packet(packet);
#endif
@ -525,10 +554,10 @@ void ServerConnection::close_connection() {
}
this->event_loop_execute_connection_close = false;
if(this->socket) {
if(this->socket)
this->socket->finalize();
}
this->protocol_handler->do_close_connection();
if(this->protocol_handler)
this->protocol_handler->do_close_connection();
this->socket = nullptr;
this->call_disconnect_result.call(0, true);
}

View File

@ -14,6 +14,8 @@ using namespace tc::connection;
extern tc::audio::AudioOutput* global_audio_output;
#define DEBUG_PREMATURE_PACKETS
#ifdef WIN32
#define _field_(name, value) value
#else
@ -210,7 +212,7 @@ VoiceClient::VoiceClient(const std::shared_ptr<VoiceConnection>&, uint16_t clien
this->output_source = global_audio_output->create_source();
this->output_source->overflow_strategy = audio::overflow_strategy::ignore;
this->output_source->max_latency = (size_t) ceil(this->output_source->sample_rate * 1);
this->output_source->min_buffer = (size_t) ceil(this->output_source->sample_rate * 0.025);
this->output_source->min_buffer = (size_t) ceil(this->output_source->sample_rate * 0.04);
this->output_source->on_underflow = [&]{
if(this->_state == state::stopping)
@ -221,14 +223,15 @@ VoiceClient::VoiceClient(const std::shared_ptr<VoiceConnection>&, uint16_t clien
log_warn(category::audio, tr("Client {} has a audio buffer underflow and not received any data for one second. Stopping replay."), this->_client_id);
} else {
if(this->_state != state::buffering) {
log_warn(category::audio, tr("Client {} has a audio buffer underflow. Buffer again."), this->_client_id);
log_warn(category::audio, tr("Client {} has a audio buffer underflow. Buffer again and try to replay prematured packets."), this->_client_id);
this->set_state(state::buffering);
}
play_premature_packets = true; /* try to replay any premature packets because we assume that the other packets got lost */
audio::decode_event_loop->schedule(static_pointer_cast<event::EventEntry>(this->ref()));
}
}
play_premature_packets = true; /* try to replay any premature packets because we assume that the other packets got lost */
audio::decode_event_loop->schedule(static_pointer_cast<event::EventEntry>(this->ref()));
return false;
};
this->output_source->on_overflow = [&](size_t count){
@ -327,8 +330,10 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
play_count++;
}
#ifdef DEBUG_PREMATURE_PACKETS
if(play_count > 0)
log_debug(category::audio, tr("Replayed {} premature packets for client {}"), play_count, this->_client_id);
log_debug(category::audio, tr("Replayed (buffer underflow) {} premature packets for client {}"), play_count, this->_client_id);
#endif
break;
}
}
@ -350,14 +355,19 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
this->process_encoded_buffer(entry);
}
if(audio_decode_event_dropped.exchange(false) && !reschedule) {
//Is not really a warning, it happens all the time and isn't really an issue
//log_warn(category::voice_connection, tr("Dropped auto enqueue event execution for client {}. No reschedulling planned, hopefully we processed all buffers."), this->_client_id);
}
if(reschedule) {
log_warn(category::voice_connection, tr("Audio data decode will take longer than {} us. Enqueueing for later"), chrono::duration_cast<chrono::microseconds>(max_time).count());
audio::decode_event_loop->schedule(static_pointer_cast<event::EventEntry>(this->ref()));
}
}
//FIXME premature packets dont work
#define MAX_LOST_PACKETS (6)
//Note: This function must be executed single threaded
void VoiceClient::process_encoded_buffer(const std::unique_ptr<EncodedBuffer> &buffer) {
string error;
@ -425,9 +435,12 @@ void VoiceClient::process_encoded_buffer(const std::unique_ptr<EncodedBuffer> &b
this->set_state(state::stopping);
/* enqueue all premature packets (list should be already ordered!) */
for(const auto& packet : codec_data->premature_packets)
this->output_source->enqueue_samples(packet.buffer);
codec_data->premature_packets.clear();
{
unique_lock buffer_lock(this->audio_decode_queue_lock);
for(const auto& packet : codec_data->premature_packets)
this->output_source->enqueue_samples(packet.buffer);
codec_data->premature_packets.clear();
}
log_trace(category::voice_connection, tr("Stopping replay for client {}. Empty buffer!"), this->_client_id);
return;
@ -442,14 +455,15 @@ void VoiceClient::process_encoded_buffer(const std::unique_ptr<EncodedBuffer> &b
if(diff <= MAX_LOST_PACKETS) {
if(diff > 0) {
log_debug(category::voice_connection,
tr("Client {} dropped one or more audio packets. Old packet id: {}, New packet id: {}, Diff: {}"),
this->_client_id, old_packet_id, buffer->packet_id, diff);
/* lets first handle packet as "lost", even thou we're enqueueing it as premature */
//auto status = codec_data->converter->decode_lost(error, diff);
//if(status < 0)
// log_warn(category::voice_connection, tr("Failed to decode (skip) dropped packets. Return code {} => {}"), status, error);
premature = this->state() != state::stopped;
premature = !buffer->head && this->state() != state::stopped;
log_debug(category::voice_connection,
tr("Client {} dropped one or more audio packets. Old packet id: {}, New packet id: {}, Diff: {}. Head: {}. Flagging chunk as premature: {}"),
this->_client_id, old_packet_id, buffer->packet_id, diff, buffer->head, premature);
}
} else {
log_debug(category::voice_connection, tr("Client {} resetted decoder. Old packet id: {}, New packet id: {}, diff: {}"), this->_client_id, old_packet_id, buffer->packet_id, diff);
@ -459,31 +473,9 @@ void VoiceClient::process_encoded_buffer(const std::unique_ptr<EncodedBuffer> &b
return;
}
}
if(!premature) {
if(!premature)
codec_data->last_packet_id = buffer->packet_id;
/* test if any premature got its original place */
{
size_t play_count = 0;
while(!codec_data->premature_packets.empty()) {
auto& packet = codec_data->premature_packets.front();
//Test if we're able to replay stuff again
if((uint16_t) (codec_data->last_packet_id + 1) < packet.packet_id)
break; //Nothing new
this->output_source->enqueue_samples(packet.buffer);
codec_data->last_packet_id = packet.packet_id;
codec_data->premature_packets.pop_front();
play_count++;
}
if(play_count > 0)
log_debug(category::audio, tr("Replayed {} premature packets for client {}"), play_count, this->_client_id);
}
}
char target_buffer[target_buffer_length];
if(target_buffer_length < codec_data->converter->expected_decoded_length(buffer->buffer.data_ptr(), buffer->buffer.length())) {
log_warn(category::voice_connection, tr("Failed to decode audio data. Target buffer is smaller then expected bytes ({} < {})"), target_buffer_length, codec_data->converter->expected_decoded_length(buffer->buffer.data_ptr(), buffer->buffer.length()));
@ -523,23 +515,55 @@ void VoiceClient::process_encoded_buffer(const std::unique_ptr<EncodedBuffer> &b
audio_buffer->sample_index = 0;
memcpy(audio_buffer->sample_data, target_buffer, this->output_source->channel_count * resampled_samples * 4);
auto it = codec_data->premature_packets.begin();
for(; it != codec_data->premature_packets.end(); it++) {
if(it->packet_id > buffer->packet_id) {
break; /* it is set to the right position */
{
unique_lock buffer_lock(this->audio_decode_queue_lock);
auto it = codec_data->premature_packets.begin();
for(; it != codec_data->premature_packets.end(); it++) {
if(it->packet_id > buffer->packet_id) {
break; /* it is set to the right position */
}
}
codec_data->premature_packets.insert(it, {
buffer->packet_id,
move(audio_buffer)
});
std::stable_sort(codec_data->premature_packets.begin(), codec_data->premature_packets.end(), [](const PrematureAudioPacket& a, const PrematureAudioPacket& b) {
return a.packet_id < b.packet_id;
});
}
codec_data->premature_packets.insert(it, {
buffer->packet_id,
audio_buffer
});
std::stable_sort(codec_data->premature_packets.begin(), codec_data->premature_packets.end(), [](const PrematureAudioPacket& a, const PrematureAudioPacket& b) {
return a.packet_id < b.packet_id;
});
} else {
auto enqueued = this->output_source->enqueue_samples(target_buffer, resampled_samples);
if(enqueued != resampled_samples)
log_warn(category::voice_connection, tr("Failed to enqueue all samples for client {}. Enqueued {} of {}"), this->_client_id, enqueued, resampled_samples);
this->set_state(state::playing);
/* test if any premature got its original place */
{
unique_lock buffer_lock(this->audio_decode_queue_lock);
size_t play_count = 0;
while(!codec_data->premature_packets.empty()) {
auto& packet = codec_data->premature_packets[0];
//Test if we're able to replay stuff again
if((uint16_t) (codec_data->last_packet_id + 1) < packet.packet_id)
break; //Nothing new
this->output_source->enqueue_samples(packet.buffer);
codec_data->last_packet_id = packet.packet_id;
codec_data->premature_packets.pop_front();
play_count++;
}
#ifdef DEBUG_PREMATURE_PACKETS
if(play_count > 0)
log_debug(category::audio, tr("Replayed (id match) {} premature packets for client {}"), play_count, this->_client_id);
#endif
}
}
}
}
void VoiceClient::event_execute_dropped(const std::chrono::system_clock::time_point &point) {
if(audio_decode_event_dropped.exchange(true))
//Is not really a warning, it happens all the time and isn't really an issue
;//log_warn(category::voice_connection, tr("Dropped auto enqueue event execution two or more times in a row for client {}"), this->_client_id);
}

View File

@ -90,7 +90,7 @@ namespace tc {
private:
struct PrematureAudioPacket {
uint16_t packet_id = 0;
std::shared_ptr<tc::audio::SampleBuffer> buffer;
std::shared_ptr<tc::audio::SampleBuffer> buffer{};
};
struct AudioCodec {
@ -138,9 +138,12 @@ namespace tc {
codec::value codec;
std::chrono::system_clock::time_point receive_timestamp;
};
std::atomic_bool audio_decode_event_dropped{false};
std::mutex audio_decode_queue_lock;
std::deque<std::unique_ptr<EncodedBuffer>> audio_decode_queue;
void event_execute(const std::chrono::system_clock::time_point &point) override;
void event_execute_dropped(const std::chrono::system_clock::time_point &point) override;
void process_encoded_buffer(const std::unique_ptr<EncodedBuffer>& /* buffer */);
};

View File

@ -121,9 +121,49 @@ std::string system_uuid() {
return _cached_system_uuid;
}
/*
"Hello World": Avg raw: 18ms Avg obf: 20ms
#include <chrono>
using namespace std::chrono;
int main() {
std::cout << "UUID: " << system_uuid() << "\n";
#define TEST_MESSAGE ("Hello World I'm a bit longer that 50 character string! Hello World I'm a bit longer that 50 character string! Hello World I'm a bit longer that 50 character string! Hello World I'm a bit longer that 50 character string! Hello World I'm a bit longer that 50 character string! Hello World I'm a bit longer that 50 character string!")
system_clock::time_point begin, end;
size_t sum_raw = 0, sum_obf = 0, runs = 50;
std::cerr << "Testing raw\n";
for(int i = 0; i < runs; i++) {
begin = system_clock::now();
for(size_t index = 0; index < 1e6; index++) {
puts(TEST_MESSAGE);
}
end = system_clock::now();
auto ms = floor<milliseconds>(end - begin).count();
sum_raw += ms;
//std::cerr << "Raw test took " << ms << "ms\n";
}
std::cerr << "Testing obf\n";
for(int i = 0; i < runs; i++) {
begin = system_clock::now();
for(size_t index = 0; index < 1e6; index++) {
puts(strobf(TEST_MESSAGE).c_str());
}
end = system_clock::now();
auto ms = floor<milliseconds>(end - begin).count();
sum_obf += ms;
//std::cerr << "Obf test took " << ms << "ms\n";
}
std::cerr << "Avg raw: " << (sum_raw / runs) << "ms Avg obf: " << (sum_obf / runs) << "ms\n";
//std::cout << "UUID: " << system_uuid() << "\n";
return 1;
}
//ADaX2mIRrC1uv83h
//ADaX2mIRrC1uv83h
*/