Fixes for 1.4.6
This commit is contained in:
parent
b92f584247
commit
d60eecd9a9
2
github
2
github
@ -1 +1 @@
|
|||||||
Subproject commit cca898581e4b4138a2b4f99429d0bbd70a344bc4
|
Subproject commit 30322f64162fd4805bc01542cc5cefbb9fe842a3
|
@ -119,7 +119,7 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
|||||||
support_latency_settings() { return true; },
|
support_latency_settings() { return true; },
|
||||||
reset_latency_settings: function() {
|
reset_latency_settings: function() {
|
||||||
const stream = this.get_stream();
|
const stream = this.get_stream();
|
||||||
stream.set_buffer_latency(0.02);
|
stream.set_buffer_latency(0.040);
|
||||||
stream.set_buffer_max_latency(0.2);
|
stream.set_buffer_max_latency(0.2);
|
||||||
return this.latency_settings();
|
return this.latency_settings();
|
||||||
},
|
},
|
||||||
|
@ -13,6 +13,56 @@ using namespace tc::audio;
|
|||||||
void AudioOutputSource::clear() {
|
void AudioOutputSource::clear() {
|
||||||
this->buffer.clear();
|
this->buffer.clear();
|
||||||
this->buffering = true;
|
this->buffering = true;
|
||||||
|
this->fade_in_start = this->buffer.write_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputSource::do_fade_out(size_t pop_count) {
|
||||||
|
if(this->will_buffer_in != -1) return;
|
||||||
|
|
||||||
|
_test_for_fade:
|
||||||
|
const auto samples_left = this->current_latency();
|
||||||
|
if(samples_left < this->fadeout_sample_length + pop_count) {
|
||||||
|
if(auto fn = this->on_underflow; fn && fn(0))
|
||||||
|
goto _test_for_fade;
|
||||||
|
|
||||||
|
auto wptr = (float*) this->buffer.write_ptr();
|
||||||
|
auto total_samples = std::min(samples_left, this->fadeout_sample_length);
|
||||||
|
if(total_samples == 0) return; //TODO Test against min_buffered_samples
|
||||||
|
|
||||||
|
for(size_t index{0}; index < total_samples; index++) {
|
||||||
|
const auto offset = (float) ((float) index / (float) total_samples);
|
||||||
|
const auto volume = log10f(1 - offset) / -2.71828182845904f;
|
||||||
|
for(int channel{0}; channel < this->channel_count; channel++)
|
||||||
|
*wptr-- *= volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_trace(category::audio, tr("Will buffer due to fade out ({} | {})"), total_samples, *(float*) this->buffer.write_ptr());
|
||||||
|
this->will_buffer_in = total_samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputSource::do_fade_in() {
|
||||||
|
if(!this->fade_in_start)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto samples_available = this->current_latency();
|
||||||
|
auto wptr = (float*) this->fade_in_start;
|
||||||
|
auto total_samples = std::min(samples_available, this->fadeout_sample_length);
|
||||||
|
if(total_samples == 0) {
|
||||||
|
log_trace(category::audio, tr("Ignoring fade in 0: {} {}"), samples_available, this->fadeout_sample_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t index{0}; index < total_samples; index++) {
|
||||||
|
const auto offset = (float) ((float) index / (float) total_samples);
|
||||||
|
const auto volume = log10f(1 - offset) / -2.71828182845904f;
|
||||||
|
for(int channel{0}; channel < this->channel_count; channel++)
|
||||||
|
*wptr++ *= volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_trace(category::audio, tr("Fade in to new buffer ({})"), total_samples);
|
||||||
|
|
||||||
|
this->fade_in_start = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
|
ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
|
||||||
@ -21,12 +71,27 @@ ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
|
|||||||
load_buffer:
|
load_buffer:
|
||||||
auto available_bytes = this->buffer.fill_count();
|
auto available_bytes = this->buffer.fill_count();
|
||||||
if(available_bytes < sizeof(float) * this->channel_count) return written;
|
if(available_bytes < sizeof(float) * this->channel_count) return written;
|
||||||
|
|
||||||
auto available_samples = available_bytes / sizeof(float) / this->channel_count;
|
auto available_samples = available_bytes / sizeof(float) / this->channel_count;
|
||||||
//log_trace(category::audio, tr("Min: {}, Max: {}, Current: {}, Buffering: {}"), this->min_buffered_samples, this->max_buffered_samples, available_samples, this->buffering);
|
|
||||||
if(this->buffering && available_samples < this->min_buffered_samples) return -2;
|
|
||||||
|
|
||||||
this->buffering = false;
|
if(this->buffering && available_samples < this->min_buffered_samples) return -2;
|
||||||
|
this->do_fade_in();
|
||||||
|
this->do_fade_out(samples); /* will also call for underflow */
|
||||||
|
//log_trace(category::audio, tr("Min: {}, Max: {}, Current: {}, Buffering: {} Required: {}, left: {}, will buffer in {}"), this->min_buffered_samples, this->max_buffered_samples, available_samples, this->buffering, samples, (int) available_samples - samples, this->will_buffer_in);
|
||||||
|
|
||||||
|
if(this->will_buffer_in > 0) {
|
||||||
|
if(samples > this->will_buffer_in) {
|
||||||
|
samples = this->will_buffer_in;
|
||||||
|
this->buffering = true;
|
||||||
|
this->fade_in_start = this->buffer.calculate_advanced_write_ptr(samples * sizeof(float) * this->channel_count);
|
||||||
|
this->will_buffer_in = -1;
|
||||||
|
log_trace(category::audio, tr("Start buffering due to fade out. Fade in ptr {}"), (void*) this->fade_in_start);
|
||||||
|
} else {
|
||||||
|
this->will_buffer_in -= samples;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->buffering = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(available_samples >= samples - written) {
|
if(available_samples >= samples - written) {
|
||||||
const auto byte_length = (samples - written) * sizeof(float) * this->channel_count;
|
const auto byte_length = (samples - written) * sizeof(float) * this->channel_count;
|
||||||
if(buffer)memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
|
if(buffer)memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
|
||||||
@ -34,10 +99,11 @@ ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
|
|||||||
|
|
||||||
if(this->on_read)
|
if(this->on_read)
|
||||||
this->on_read();
|
this->on_read();
|
||||||
|
|
||||||
return samples;
|
return samples;
|
||||||
} else {
|
} else {
|
||||||
const auto byte_length = available_samples * sizeof(float) * this->channel_count;
|
const auto byte_length = available_samples * sizeof(float) * this->channel_count;
|
||||||
if(buffer)memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
|
if(buffer) memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
|
||||||
this->buffer.advance_read_ptr(byte_length);
|
this->buffer.advance_read_ptr(byte_length);
|
||||||
written += available_samples;
|
written += available_samples;
|
||||||
written_bytes += byte_length;
|
written_bytes += byte_length;
|
||||||
@ -48,14 +114,58 @@ ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
|
|||||||
if(fn(samples - written))
|
if(fn(samples - written))
|
||||||
goto load_buffer;
|
goto load_buffer;
|
||||||
|
|
||||||
if(buffer)memset(buffer, 0, (samples - written) * sizeof(float) * this->channel_count);
|
if(buffer)
|
||||||
|
memset((char*) buffer + written_bytes, 0, (samples - written) * sizeof(float) * this->channel_count);
|
||||||
|
|
||||||
this->buffering = true;
|
this->buffering = true;
|
||||||
|
this->fade_in_start = this->buffer.write_ptr();
|
||||||
|
log_trace(category::audio, tr("Start buffering due to underflow."), (void*) this->fade_in_start);
|
||||||
|
this->will_buffer_in = -1;
|
||||||
|
|
||||||
if(this->on_read)
|
if(this->on_read)
|
||||||
this->on_read();
|
this->on_read();
|
||||||
return written; /* return the written samples */
|
return written; /* return the written samples */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t AudioOutputSource::enqueue_silence(size_t samples) {
|
||||||
|
size_t enqueued{0};
|
||||||
|
|
||||||
|
auto free_bytes = this->buffer.free_count();
|
||||||
|
auto free_samples = free_bytes / sizeof(float) / this->channel_count;
|
||||||
|
if(this->max_buffered_samples && free_samples > this->max_buffered_samples) free_samples = this->max_buffered_samples;
|
||||||
|
|
||||||
|
if(free_samples >= samples) {
|
||||||
|
const auto byte_length = samples * sizeof(float) * this->channel_count;
|
||||||
|
memset(this->buffer.write_ptr(), 0, byte_length);
|
||||||
|
this->buffer.advance_write_ptr(byte_length);
|
||||||
|
return samples;
|
||||||
|
} else {
|
||||||
|
const auto byte_length = free_samples * sizeof(float) * this->channel_count;
|
||||||
|
memset(this->buffer.write_ptr(), 0, byte_length);
|
||||||
|
this->buffer.advance_write_ptr(byte_length);
|
||||||
|
enqueued += free_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto fn = this->on_overflow; fn)
|
||||||
|
fn(samples - enqueued);
|
||||||
|
|
||||||
|
switch (this->overflow_strategy) {
|
||||||
|
case overflow_strategy::discard_input:
|
||||||
|
return -2;
|
||||||
|
case overflow_strategy::discard_buffer_all:
|
||||||
|
this->buffer.clear();
|
||||||
|
break;
|
||||||
|
case overflow_strategy::discard_buffer_half:
|
||||||
|
this->buffer.advance_read_ptr(this->buffer.fill_count() / 2);
|
||||||
|
break;
|
||||||
|
case overflow_strategy::ignore:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->fade_in_start = this->buffer.write_ptr(); /* so we fade in from silence */
|
||||||
|
return enqueued;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t AudioOutputSource::enqueue_samples(const void *buffer, size_t samples) {
|
ssize_t AudioOutputSource::enqueue_samples(const void *buffer, size_t samples) {
|
||||||
size_t enqueued{0};
|
size_t enqueued{0};
|
||||||
|
|
||||||
|
@ -50,8 +50,12 @@ namespace tc::audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool buffering{true};
|
bool buffering{true};
|
||||||
|
char* fade_in_start{nullptr};
|
||||||
|
ssize_t will_buffer_in{-1};
|
||||||
size_t min_buffered_samples{0};
|
size_t min_buffered_samples{0};
|
||||||
size_t max_buffered_samples{0};
|
size_t max_buffered_samples{0};
|
||||||
|
|
||||||
|
size_t fadeout_sample_length{360};
|
||||||
|
|
||||||
overflow_strategy::value overflow_strategy = overflow_strategy::discard_buffer_half;
|
overflow_strategy::value overflow_strategy = overflow_strategy::discard_buffer_half;
|
||||||
|
|
||||||
@ -62,14 +66,19 @@ namespace tc::audio {
|
|||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
ssize_t pop_samples(void* /* output buffer */, size_t /* sample count */);
|
ssize_t pop_samples(void* /* output buffer */, size_t /* sample count */);
|
||||||
|
ssize_t enqueue_silence(size_t /* sample count */);
|
||||||
ssize_t enqueue_samples(const void * /* input buffer */, size_t /* sample count */);
|
ssize_t enqueue_samples(const void * /* input buffer */, size_t /* sample count */);
|
||||||
ssize_t enqueue_samples_no_interleave(const void * /* input buffer */, size_t /* sample count */);
|
ssize_t enqueue_samples_no_interleave(const void * /* input buffer */, size_t /* sample count */);
|
||||||
private:
|
private:
|
||||||
AudioOutputSource(AudioOutput* handle, size_t channel_count, size_t sample_rate, ssize_t max_buffer_sample_count = -1) :
|
AudioOutputSource(AudioOutput* handle, size_t channel_count, size_t sample_rate, ssize_t max_buffer_sample_count = -1) :
|
||||||
handle(handle), channel_count(channel_count), sample_rate(sample_rate),
|
handle(handle), channel_count(channel_count), sample_rate(sample_rate),
|
||||||
buffer{max_buffer_sample_count == -1 ? channel_count * sample_rate * sizeof(float) : max_buffer_sample_count * channel_count * sizeof(float)} {
|
buffer{max_buffer_sample_count == -1 ? channel_count * sample_rate * sizeof(float) : max_buffer_sample_count * channel_count * sizeof(float)} {
|
||||||
|
this->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_fade_out(size_t /* pop count */);
|
||||||
|
void do_fade_in();
|
||||||
|
|
||||||
tc::ring_buffer buffer;
|
tc::ring_buffer buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,12 +40,12 @@ namespace tc {
|
|||||||
/**
|
/**
|
||||||
* @return number of bytes written on success
|
* @return number of bytes written on success
|
||||||
*/
|
*/
|
||||||
virtual ssize_t encode(std::string& /* error */, const void* /* source */, void* /* destination */, size_t /* destination byte length */) = 0;
|
virtual ssize_t encode(std::string& /* error */, const void* /* source */, void* /* destination */, size_t /* destination byte length */, bool /* head package */) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return number of samples on success
|
* @return number of samples on success
|
||||||
*/
|
*/
|
||||||
virtual ssize_t decode(std::string& /* error */, const void* /* source */, size_t /* source byte length */, void* /* destination */) = 0;
|
virtual ssize_t decode(std::string& /* error */, const void* /* source */, size_t /* source byte length */, void* /* destination */, bool /* fec decoding */) = 0;
|
||||||
virtual ssize_t decode_lost(std::string& /* error */, size_t /* packets */) = 0;
|
virtual ssize_t decode_lost(std::string& /* error */, size_t /* packets */) = 0;
|
||||||
|
|
||||||
virtual size_t expected_encoded_length(size_t /* sample count */) = 0;
|
virtual size_t expected_encoded_length(size_t /* sample count */) = 0;
|
||||||
|
@ -29,7 +29,7 @@ bool OpusConverter::initialize(std::string &error, int application_type) {
|
|||||||
void OpusConverter::reset_encoder() {
|
void OpusConverter::reset_encoder() {
|
||||||
lock_guard lock(this->coder_lock);
|
lock_guard lock(this->coder_lock);
|
||||||
|
|
||||||
|
log_info(category::audio, tr("Resetting encoder"));
|
||||||
auto result = opus_encoder_ctl(this->encoder, OPUS_RESET_STATE);
|
auto result = opus_encoder_ctl(this->encoder, OPUS_RESET_STATE);
|
||||||
if(result != OPUS_OK)
|
if(result != OPUS_OK)
|
||||||
log_warn(category::audio, tr("Failed to reset opus encoder. Opus result: {}"), result);
|
log_warn(category::audio, tr("Failed to reset opus encoder. Opus result: {}"), result);
|
||||||
@ -38,9 +38,11 @@ void OpusConverter::reset_encoder() {
|
|||||||
void OpusConverter::reset_decoder() {
|
void OpusConverter::reset_decoder() {
|
||||||
lock_guard lock(this->coder_lock);
|
lock_guard lock(this->coder_lock);
|
||||||
|
|
||||||
|
log_info(category::audio, tr("Resetting decoder"));
|
||||||
auto result = opus_decoder_ctl(this->decoder, OPUS_RESET_STATE);
|
auto result = opus_decoder_ctl(this->decoder, OPUS_RESET_STATE);
|
||||||
if(result != OPUS_OK)
|
if(result != OPUS_OK)
|
||||||
log_warn(category::audio, tr("Failed to reset opus decoder. Opus result: {}"), result);
|
log_warn(category::audio, tr("Failed to reset opus decoder. Opus result: {}"), result);
|
||||||
|
this->fec_decoder_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -54,9 +56,10 @@ void OpusConverter::finalize() {
|
|||||||
this->decoder = nullptr;
|
this->decoder = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t OpusConverter::encode(std::string &error, const void *source, void *target, size_t target_length) {
|
ssize_t OpusConverter::encode(std::string &error, const void *source, void *target, size_t target_length, bool head_package) {
|
||||||
lock_guard lock(this->coder_lock);
|
lock_guard lock(this->coder_lock);
|
||||||
|
|
||||||
|
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(head_package ? 100 : 15));
|
||||||
auto result = opus_encode_float(this->encoder, (float*) source, (int) this->_frame_size, (uint8_t*) target, (opus_int32) target_length);
|
auto result = opus_encode_float(this->encoder, (float*) source, (int) this->_frame_size, (uint8_t*) target, (opus_int32) target_length);
|
||||||
if(result < OPUS_OK) {
|
if(result < OPUS_OK) {
|
||||||
error = to_string(result) + "|" + opus_strerror(result);
|
error = to_string(result) + "|" + opus_strerror(result);
|
||||||
@ -65,10 +68,10 @@ ssize_t OpusConverter::encode(std::string &error, const void *source, void *targ
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t OpusConverter::decode(std::string &error, const void *source, size_t source_length, void *target) {
|
ssize_t OpusConverter::decode(std::string &error, const void *source, size_t source_length, void *target, bool use_fec) {
|
||||||
lock_guard lock(this->coder_lock);
|
lock_guard lock(this->coder_lock);
|
||||||
|
|
||||||
auto result = opus_decode_float(this->decoder, (uint8_t*) source, (opus_int32) source_length, (float*) target, (int) this->_frame_size, 0);
|
auto result = opus_decode_float(this->decoder, (uint8_t*) source, (opus_int32) source_length, (float*) target, (int) this->_frame_size, use_fec ? 1 : 0);
|
||||||
if(result < OPUS_OK) {
|
if(result < OPUS_OK) {
|
||||||
error = to_string(result) + "|" + opus_strerror(result);
|
error = to_string(result) + "|" + opus_strerror(result);
|
||||||
return -1;
|
return -1;
|
||||||
@ -85,6 +88,7 @@ ssize_t OpusConverter::decode_lost(std::string &error, size_t packets) {
|
|||||||
if(result < OPUS_OK)
|
if(result < OPUS_OK)
|
||||||
log_warn(category::audio, tr("Opus decode lost resulted in error: {}"), result);
|
log_warn(category::audio, tr("Opus decode lost resulted in error: {}"), result);
|
||||||
}
|
}
|
||||||
|
this->fec_decoder_ = true;
|
||||||
free(buffer);
|
free(buffer);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -123,6 +127,19 @@ bool OpusConverter::_initialize_encoder(std::string &error) {
|
|||||||
error = "failed to set bitrate (" + to_string(error_id) + ")";
|
error = "failed to set bitrate (" + to_string(error_id) + ")";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_id = opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1));
|
||||||
|
if(error_id) {
|
||||||
|
error = "failed to enable fec (" + to_string(error_id) + ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_id = opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(15));
|
||||||
|
if(error_id) {
|
||||||
|
error = "failed to assume a 15% packet loss (" + to_string(error_id) + ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ namespace tc {
|
|||||||
void reset_encoder() override;
|
void reset_encoder() override;
|
||||||
void reset_decoder() override;
|
void reset_decoder() override;
|
||||||
|
|
||||||
ssize_t encode(std::string & /* error */, const void * /* source */, void * /* target */, size_t /* target size */) override;
|
ssize_t encode(std::string & /* error */, const void * /* source */, void * /* target */, size_t /* target size */, bool /* head package */) override;
|
||||||
ssize_t decode(std::string & /* error */, const void * /* source */, size_t /* source size */, void *pVoid1) override;
|
ssize_t decode(std::string & /* error */, const void * /* source */, size_t /* source size */, void *pVoid1, bool /* use fec */) override;
|
||||||
|
|
||||||
ssize_t decode_lost(std::string &string, size_t /* packets */) override;
|
ssize_t decode_lost(std::string &string, size_t /* packets */) override;
|
||||||
|
|
||||||
@ -31,6 +31,7 @@ namespace tc {
|
|||||||
OpusDecoder* decoder = nullptr;
|
OpusDecoder* decoder = nullptr;
|
||||||
OpusEncoder* encoder = nullptr;
|
OpusEncoder* encoder = nullptr;
|
||||||
|
|
||||||
|
bool fec_decoder_{false};
|
||||||
int _application_type = 0;
|
int _application_type = 0;
|
||||||
|
|
||||||
bool _finalize_encoder(std::string& /* error */);
|
bool _finalize_encoder(std::string& /* error */);
|
||||||
|
@ -211,7 +211,7 @@ namespace tc::audio::sounds {
|
|||||||
if(current_size > max_size) return false;
|
if(current_size > max_size) return false;
|
||||||
|
|
||||||
const auto size_left = max_size - current_size;
|
const auto size_left = max_size - current_size;
|
||||||
return size_left >= this->cache_buffer_sample_size();
|
return size_left >= this->cache_buffer_sample_size() * 1.5; /* ensure we've a bit more space */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,8 +164,13 @@ void VoiceSender::encode_raw_frame(const std::unique_ptr<AudioFrame> &frame) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(codec_data)
|
if(codec_data) {
|
||||||
codec_data->packet_counter = 0;
|
codec_data->packet_counter = 0;
|
||||||
|
if(auto converter = codec_data->converter; converter) {
|
||||||
|
log_trace(category::voice_connection, tr("Resetting encoder"));
|
||||||
|
converter->reset_encoder();
|
||||||
|
}
|
||||||
|
}
|
||||||
auto server = this->handle->handle();
|
auto server = this->handle->handle();
|
||||||
server->send_voice_data(this->_buffer, 0, codec, flag_head);
|
server->send_voice_data(this->_buffer, 0, codec, flag_head);
|
||||||
return;
|
return;
|
||||||
@ -205,7 +210,7 @@ void VoiceSender::encode_raw_frame(const std::unique_ptr<AudioFrame> &frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char _packet_buffer[512];
|
char _packet_buffer[512];
|
||||||
auto encoded_bytes = codec_data->converter->encode(error, this->_buffer, _packet_buffer, 512);
|
auto encoded_bytes = codec_data->converter->encode(error, this->_buffer, _packet_buffer, 512, flag_head);
|
||||||
if(encoded_bytes <= 0) {
|
if(encoded_bytes <= 0) {
|
||||||
log_error(category::voice_connection, tr("Failed to encode voice: {}"), error);
|
log_error(category::voice_connection, tr("Failed to encode voice: {}"), error);
|
||||||
return;
|
return;
|
||||||
|
@ -321,6 +321,12 @@ inline constexpr uint16_t packet_id_diff(uint16_t lower, uint16_t upper) {
|
|||||||
#define MAX_LOST_PACKETS (6)
|
#define MAX_LOST_PACKETS (6)
|
||||||
#define target_buffer_length 16384
|
#define target_buffer_length 16384
|
||||||
void VoiceClient::process_packet(uint16_t packet_id, const pipes::buffer_view& buffer, codec::value codec, bool is_head) {
|
void VoiceClient::process_packet(uint16_t packet_id, const pipes::buffer_view& buffer, codec::value codec, bool is_head) {
|
||||||
|
#if 0
|
||||||
|
if(rand() % 10 == 0) {
|
||||||
|
log_info(category::audio, tr("Dropping audio packet id {}"), packet_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#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;
|
||||||
@ -488,7 +494,7 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
|
|||||||
replay_head = audio_codec.pending_buffers;
|
replay_head = audio_codec.pending_buffers;
|
||||||
audio_codec.pending_buffers = skip_ptr[SKIP_SEQ_LENGTH];
|
audio_codec.pending_buffers = skip_ptr[SKIP_SEQ_LENGTH];
|
||||||
skip_ptr[SKIP_SEQ_LENGTH - 1]->next = nullptr;
|
skip_ptr[SKIP_SEQ_LENGTH - 1]->next = nullptr;
|
||||||
log_trace(category::voice_connection, tr("Skipping from {} to {} because of {} packets in a row"), audio_codec.last_packet_id, head->packet_id, SKIP_SEQ_LENGTH);
|
log_trace(category::voice_connection, tr("Skipping from {} to {} because of {} packets in a row"), audio_codec.last_packet_id, replay_head->packet_id, SKIP_SEQ_LENGTH);
|
||||||
|
|
||||||
/* Do not set process_pending to false, because we're not done
|
/* Do not set process_pending to false, because we're not done
|
||||||
* We're just replaying all loose packets which are not within a sequence until we reach a sequence
|
* We're just replaying all loose packets which are not within a sequence until we reach a sequence
|
||||||
@ -508,7 +514,7 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
|
|||||||
audio_codec.pending_buffers = head->next;
|
audio_codec.pending_buffers = head->next;
|
||||||
head->next = nullptr;
|
head->next = nullptr;
|
||||||
log_trace(category::voice_connection, tr("Skipping from {} to {} because of over 6 packets between"),
|
log_trace(category::voice_connection, tr("Skipping from {} to {} because of over 6 packets between"),
|
||||||
audio_codec.last_packet_id, head->packet_id);
|
audio_codec.last_packet_id, replay_head->packet_id);
|
||||||
/* do not negate process_pending here. Same reason as with the 3 sequence */
|
/* do not negate process_pending here. Same reason as with the 3 sequence */
|
||||||
} else {
|
} else {
|
||||||
/* no packets we're willing to replay */
|
/* no packets we're willing to replay */
|
||||||
@ -542,27 +548,60 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
|
|||||||
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 if(local_last_pid > replay_head->packet_id) {
|
|
||||||
log_debug(category::voice_connection, tr("Client {} tried to replay too old voice chunk. Current id is {}, attempted chunk id is {}. Dropping buffer."), this->_client_id, local_last_pid, replay_head->packet_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 > 6) {
|
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. Skipping ahead."), 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);
|
||||||
|
*/
|
||||||
|
auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer, true);
|
||||||
|
if(decoded)
|
||||||
|
this->output_source->enqueue_samples(decoded->sample_data, decoded->sample_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(replay_head->reset_decoder)
|
const auto is_new_audio_stream = this->_state != state::buffering && this->_state != state::playing;
|
||||||
audio_codec.converter->reset_decoder();
|
if(replay_head->reset_decoder || is_new_audio_stream) {
|
||||||
|
audio_codec.converter->reset_decoder();
|
||||||
|
replay_head->reset_decoder = false;
|
||||||
|
|
||||||
|
#if 1 /* Better approch */
|
||||||
|
/* initialize with last packet */
|
||||||
|
char target_buffer[target_buffer_length];
|
||||||
|
if(target_buffer_length > audio_codec.converter->expected_decoded_length(replay_head->buffer.data_ptr(), replay_head->buffer.length())) {
|
||||||
|
audio_codec.converter->decode(error, replay_head->buffer.data_ptr(), replay_head->buffer.length(), target_buffer, 1);
|
||||||
|
} else {
|
||||||
|
//TODO: May a small warning here?
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 /* (maybe) TS3 approch */
|
||||||
|
if(replay_head->head) {
|
||||||
|
/* initialize with last packet */
|
||||||
|
char target_buffer[target_buffer_length];
|
||||||
|
if(target_buffer_length > audio_codec.converter->expected_decoded_length(replay_head->buffer.data_ptr(), replay_head->buffer.length())) {
|
||||||
|
audio_codec.converter->decode(error, replay_head->buffer.data_ptr(), replay_head->buffer.length(), target_buffer, 1);
|
||||||
|
} else {
|
||||||
|
//TODO: May a small warning here?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//TODO: Use statically allocated buffer?
|
//TODO: Use statically allocated buffer?
|
||||||
auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer);
|
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) {
|
||||||
|
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_samples(decoded->sample_data, decoded->sample_size);
|
this->output_source->enqueue_samples(decoded->sample_data, decoded->sample_size);
|
||||||
this->set_state(state::playing);
|
this->set_state(state::playing);
|
||||||
}
|
}
|
||||||
@ -613,7 +652,7 @@ void VoiceClient::initialize_code(const codec::value &audio_codec) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
codec_data.resampler = make_shared<audio::AudioResampler>(codec_data.converter->sample_rate(), this->output_source->sample_rate, codec_data.converter->channels());
|
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;
|
||||||
@ -623,7 +662,7 @@ void VoiceClient::initialize_code(const codec::value &audio_codec) {
|
|||||||
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) {
|
std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::value &audio_codec, const pipes::buffer_view &buffer, bool fec) {
|
||||||
assert(this->output_source);
|
assert(this->output_source);
|
||||||
|
|
||||||
auto& codec_data = this->codec[audio_codec];
|
auto& codec_data = this->codec[audio_codec];
|
||||||
@ -638,13 +677,19 @@ std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::val
|
|||||||
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.data_ptr(), 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.data_ptr(), buffer.length()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto samples = codec_data.converter->decode(error, buffer.data_ptr(), buffer.length(), target_buffer);
|
auto samples = codec_data.converter->decode(error, buffer.data_ptr(), buffer.length(), target_buffer, fec);
|
||||||
if(samples < 0) {
|
if(samples < 0) {
|
||||||
log_warn(category::voice_connection, tr("Failed to decode audio data: {}"), error);
|
log_warn(category::voice_connection, tr("Failed to decode audio data: {}"), error);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if(target_buffer_length < codec_data.resampler->estimated_output_size(samples) * codec_data.resampler->channels() * 4) {
|
|
||||||
log_warn(category::voice_connection, tr("Failed to resample audio data. Target buffer is smaller then expected bytes ({} < {})"), target_buffer_length, (codec_data.resampler->estimated_output_size(samples) * codec_data.resampler->channels() * 4));
|
if(!audio::merge::merge_channels_interleaved(target_buffer, this->output_source->channel_count, target_buffer, codec_data.converter->channels(), samples)) {
|
||||||
|
log_warn(category::voice_connection, tr("Failed to merge channels to output stream channel count!"));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target_buffer_length < codec_data.resampler->estimated_output_size(samples) * this->output_source->channel_count * sizeof(float)) {
|
||||||
|
log_warn(category::voice_connection, tr("Failed to resample audio data. Target buffer is smaller then expected bytes ({} < {})"), target_buffer_length, (codec_data.resampler->estimated_output_size(samples) * this->output_source->channel_count * 4));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,11 +699,6 @@ std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::val
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!audio::merge::merge_channels_interleaved(target_buffer, this->output_source->channel_count, target_buffer, codec_data.resampler->channels(), resampled_samples)) {
|
|
||||||
log_warn(category::voice_connection, tr("Failed to merge channels to output stream channel count!"));
|
|
||||||
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;
|
||||||
|
@ -164,7 +164,7 @@ namespace tc::connection {
|
|||||||
void event_execute_dropped(const std::chrono::system_clock::time_point &point) override;
|
void event_execute_dropped(const std::chrono::system_clock::time_point &point) override;
|
||||||
|
|
||||||
/* its recommend to call this in correct packet oder */
|
/* its recommend to call this in correct packet oder */
|
||||||
std::shared_ptr<audio::SampleBuffer> decode_buffer(const codec::value& /* codec */,const pipes::buffer_view& /* buffer */);
|
std::shared_ptr<audio::SampleBuffer> decode_buffer(const codec::value& /* codec */,const pipes::buffer_view& /* buffer */, bool /* fec */);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -238,6 +238,11 @@ namespace tc {
|
|||||||
assert(this->fill_count() >= 0);
|
assert(this->fill_count() >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* ring_buffer::calculate_advanced_write_ptr(size_t bytes) {
|
||||||
|
auto offset{this->write_offset.load() + bytes};
|
||||||
|
return this->memory.address + (offset % this->memory.capacity);
|
||||||
|
}
|
||||||
|
|
||||||
const void* ring_buffer::read_ptr() const {
|
const void* ring_buffer::read_ptr() const {
|
||||||
auto offset{this->read_offset.load()};
|
auto offset{this->read_offset.load()};
|
||||||
return this->memory.address + (offset % this->memory.capacity);
|
return this->memory.address + (offset % this->memory.capacity);
|
||||||
|
@ -18,6 +18,8 @@ namespace tc {
|
|||||||
char* write_ptr();
|
char* write_ptr();
|
||||||
void advance_write_ptr(size_t /* count */);
|
void advance_write_ptr(size_t /* count */);
|
||||||
|
|
||||||
|
char* calculate_advanced_write_ptr(size_t /* count */bytes);
|
||||||
|
|
||||||
/* do not read more than the capacity! */
|
/* do not read more than the capacity! */
|
||||||
[[nodiscard]] const void* read_ptr() const;
|
[[nodiscard]] const void* read_ptr() const;
|
||||||
void advance_read_ptr(size_t /* count */);
|
void advance_read_ptr(size_t /* count */);
|
||||||
|
@ -620,3 +620,14 @@
|
|||||||
[59B2][2] Rollback done
|
[59B2][2] Rollback done
|
||||||
[59B2][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
|
[59B2][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
|
||||||
[59B2][2] ----------- log ended at 2020-04-22.20:01:11 -----------
|
[59B2][2] ----------- log ended at 2020-04-22.20:01:11 -----------
|
||||||
|
[5EA7][2] ----------- log started at 2020-04-22.20:01:37 -----------
|
||||||
|
[5EA7][2] App executed as admin: no
|
||||||
|
[5EA7][2] loading config from file updater\test\dummy_config.json
|
||||||
|
[5EA7][1] Loaded 1 locking actions and 1 moving actions
|
||||||
|
[5EA7][2] Awaiting the unlocking of all files
|
||||||
|
[5EA7][5] Failed to lock file D:\Program Files\IDA 7.0\ida.exe. Timeout: 5000, Time tried: 70663
|
||||||
|
[5EA7][2] Rollbacking 0 moved files
|
||||||
|
[5EA7][2] Rollbacking 0 deleted files
|
||||||
|
[5EA7][2] Rollback done
|
||||||
|
[5EA7][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6U0dWc2JHOGdWMjl5YkdRZ1pYSnliM0k9O2Vycm9yX21lc3NhZ2U6Wm1GcGJHVmtJSFJ2SUd4dlkyc2dabWxzWlE9PQ==
|
||||||
|
[5EA7][2] ----------- log ended at 2020-04-22.20:02:48 -----------
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "TeaClient",
|
"name": "TeaClient",
|
||||||
"version": "1.4.5-3",
|
"version": "1.4.6",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Loading…
Reference in New Issue
Block a user