TeaSpeak-Client/native/serverconnection/src/connection/audio/VoiceClient.h

201 lines
6.8 KiB
C++

#pragma once
#include <array>
#include <nan.h>
#include <include/NanEventCallback.h>
#include <functional>
#include <pipes/buffer.h>
#include "../../audio/AudioResampler.h"
#include "../../audio/codec/Converter.h"
#include "../../audio/AudioOutput.h"
#include "../../EventLoop.h"
namespace tc::connection {
class ServerConnection;
class VoiceConnection;
class VoiceClient;
namespace codec {
enum value {
MIN = 0,
SPEEX_NARROWBAND = 0,
SPEEX_WIDEBAND = 1,
SPEEX_ULTRA_WIDEBAND = 2,
CELT_MONO = 3,
OPUS_VOICE = 4,
OPUS_MUSIC = 5,
MAX = 5,
};
struct condec_info {
bool supported;
std::string name;
std::function<std::shared_ptr<audio::codec::Converter>(std::string&)> new_converter;
};
extern const condec_info info[6];
inline const condec_info* get_info(value codec) {
if(codec > value::MAX || codec < value::MIN)
return nullptr;
return &info[codec];
}
}
class VoiceClient : private event::EventEntry {
friend class VoiceConnection;
#ifdef WIN32
template<typename _Tp, typename _Up>
friend _NODISCARD std::shared_ptr<_Tp> std::static_pointer_cast(std::shared_ptr<_Up>&& _Other) noexcept;
#else
template<typename _Tp, typename _Up>
friend inline std::shared_ptr<_Tp> std::static_pointer_cast(const std::shared_ptr<_Up>& __r) noexcept;
#endif
public:
struct state {
enum value {
buffering, /* this state is never active */
playing,
stopping,
stopped
};
};
VoiceClient(const std::shared_ptr<VoiceConnection>& /* connection */, uint16_t /* client id */);
virtual ~VoiceClient();
void initialize();
inline uint16_t client_id() const { return this->client_id_; }
void initialize_js_object();
void finalize_js_object();
v8::Local<v8::Object> js_handle() {
assert(v8::Isolate::GetCurrent());
return this->js_handle_.Get(Nan::GetCurrentContext()->GetIsolate());
}
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 execute_tick();
inline float get_volume() const { return this->volume_; }
inline void set_volume(float value) { this->volume_ = value; }
inline state::value state() { return this->state_; }
void cancel_replay();
std::function<void()> on_state_changed;
inline std::shared_ptr<audio::AudioOutputSource> output_stream() { return this->output_source; }
private:
struct EncodedBuffer {
bool head{false};
bool reset_decoder{false};
std::chrono::system_clock::time_point receive_timestamp;
pipes::buffer buffer;
codec::value codec{codec::MIN};
uint16_t packet_id{0};
EncodedBuffer* next{nullptr};
};
struct AudioCodec {
enum struct State {
UNINITIALIZED,
INITIALIZED_SUCCESSFULLY,
INITIALIZED_FAIL,
UNSUPPORTED,
};
codec::value codec{};
uint16_t last_packet_id{0xFFFF}; /* the first packet id is 0 so one packet before is 0xFFFF */
std::chrono::system_clock::time_point last_packet_timestamp;
inline std::chrono::system_clock::time_point stream_timeout() const {
return this->last_packet_timestamp + std::chrono::milliseconds{1000};
}
State state{State::UNINITIALIZED};
std::shared_ptr<audio::codec::Converter> converter{nullptr};
std::shared_ptr<audio::AudioResampler> resampler{nullptr};
std::mutex pending_lock{};
EncodedBuffer* pending_buffers{nullptr};
/* forces all packets which are within the next chain to replay until (inclusive) force_replay is reached */
EncodedBuffer* force_replay{nullptr};
bool process_pending{false};
};
std::array<AudioCodec, codec::MAX + 1> codec{};
void initialize_code(const codec::value& /* codec */);
/* might be null (if audio hasn't been initialized) */
std::shared_ptr<audio::AudioOutputSource> output_source;
std::weak_ptr<VoiceClient> ref_;
v8::Persistent<v8::Object> js_handle_;
uint16_t client_id_{0};
float volume_{1.f};
std::chrono::system_clock::time_point _last_received_packet;
state::value state_ = state::stopped;
inline void set_state(state::value value) {
if(value == this->state_) {
return;
}
this->state_ = value;
if(this->on_state_changed) {
this->on_state_changed();
}
}
std::atomic_bool audio_decode_event_dropped{false};
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;
/* 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 */, bool /* fec */);
};
class VoiceClientWrap : public Nan::ObjectWrap {
public:
static NAN_MODULE_INIT(Init);
static NAN_METHOD(NewInstance);
static inline Nan::Persistent<v8::Function> & constructor() {
static Nan::Persistent<v8::Function> my_constructor;
return my_constructor;
}
explicit VoiceClientWrap(const std::shared_ptr<VoiceClient>&);
~VoiceClientWrap() override;
void do_wrap(const v8::Local<v8::Object>&);
private:
static NAN_METHOD(_get_state);
static NAN_METHOD(_get_volume);
static NAN_METHOD(_set_volume);
static NAN_METHOD(_abort_replay);
static NAN_METHOD(_get_stream);
std::weak_ptr<VoiceClient> _handle;
bool currently_playing_{false};
Nan::callback_t<> call_state_changed;
void call_state_changed_();
};
}