#include "AudioSender.h" #include "VoiceConnection.h" #include "../ServerConnection.h" #include "../../logger.h" #include "AudioEventLoop.h" using namespace std; using namespace tc; using namespace tc::audio; using namespace tc::audio::codec; using namespace tc::connection; VoiceSender::VoiceSender(tc::connection::VoiceConnection *handle) : handle(handle) {} VoiceSender::~VoiceSender() { audio::encode_event_loop->cancel(static_pointer_cast(this->_ref.lock())); this->clear_buffer(); /* buffer might be accessed within encode_raw_frame, but this could not be trigered while this will be deallocated! */ } bool VoiceSender::initialize_codec(std::string& error, connection::codec::value codec, size_t channels, size_t rate) { auto& data = this->codec[codec]; bool new_allocated = !data; if(new_allocated) data = make_unique(); data->successfully_initialized = false; auto info = codec::get_info(codec); if(!info || !info->supported) { if(new_allocated) log_error(category::voice_connection, tr("Tried to send voice packet but we dont support the current codec ({})"), codec); return false; } if(!data->converter || data->converter->channels() != channels || data->converter->sample_rate() != rate) { data->converter = info->new_converter(error); if(!data->converter) return false; } if(!data->resampler || data->resampler->input_rate() != rate) data->resampler = make_shared(rate, data->converter->sample_rate(), data->converter->channels()); if(!data->resampler->valid()) { error = "resampler is invalid"; return false; } data->successfully_initialized = true; return true; } void VoiceSender::send_data(const void *data, size_t samples, size_t rate, size_t channels) { unique_lock lock(this->_execute_lock); if(!this->handle) { log_warn(category::voice_connection, tr("Dropping raw audio frame because of an invalid handle.")); return; } auto frame = make_unique(); frame->sample_rate = rate; frame->channels = channels; frame->buffer = pipes::buffer{(void*) data, samples * channels * 4}; frame->timestamp = chrono::system_clock::now(); /* { lock_guard buffer_lock(this->raw_audio_buffer_lock); this->raw_audio_buffers.push_back(move(frame)); } audio::encode_event_loop->schedule(static_pointer_cast(this->_ref.lock())); */ lock.unlock(); encode_raw_frame(frame); } void VoiceSender::send_stop() { lock_guard lock(this->_execute_lock); auto server = this->handle->handle(); server->send_voice_data(nullptr, 0, this->_current_codec, false); } void VoiceSender::finalize() { lock_guard lock(this->_execute_lock); this->handle = nullptr; } void VoiceSender::event_execute(const std::chrono::system_clock::time_point &point) { static auto max_time = chrono::milliseconds(10); bool reschedule = false; auto now = chrono::system_clock::now(); while(true) { unique_lock buffer_lock(this->raw_audio_buffer_lock); if(this->raw_audio_buffers.empty()) break; if(chrono::system_clock::now() - now > max_time) { reschedule = true; break; } auto entry = move(this->raw_audio_buffers.front()); this->raw_audio_buffers.pop_front(); buffer_lock.unlock(); //TODO: Drop too old buffers! this->encode_raw_frame(entry); } if(reschedule) { log_warn(category::voice_connection, tr("Audio data decode will take longer than {} us. Enqueueing for later"), chrono::duration_cast(max_time).count()); audio::decode_event_loop->schedule(static_pointer_cast(this->_ref.lock())); } } void VoiceSender::encode_raw_frame(const std::unique_ptr &frame) { auto codec = this->_current_codec; auto& codec_data = this->codec[codec]; string error; if(!this->initialize_codec(error, codec, frame->channels, frame->sample_rate)) { log_error(category::voice_connection, tr("Failed to initialize codec: {}"), error); return; } /* TODO: May test for channel and sample rate? */ this->ensure_buffer(codec_data->resampler->estimated_output_size(frame->buffer.length())); auto resampled_samples = codec_data->resampler->process(this->_buffer, frame->buffer.data_ptr(), frame->buffer.length() / frame->channels / 4); if(resampled_samples <= 0) { log_error(category::voice_connection, tr("Resampler returned {}"), resampled_samples); return; } auto encoded_bytes = codec_data->converter->encode(error, this->_buffer, this->_buffer, this->_buffer_size); if(encoded_bytes <= 0) { log_error(category::voice_connection, tr("Failed to encode voice: {}"), error); return; } { lock_guard lock(this->_execute_lock); if(!this->handle) { log_warn(category::voice_connection, tr("Dropping audio frame because of an invalid handle.")); return; } auto server = this->handle->handle(); server->send_voice_data(this->_buffer, encoded_bytes, codec, false); } }