2019-08-21 10:00:27 +02:00

166 lines
4.5 KiB
C++

#pragma once
#include <array>
#include <nan.h>
#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 {
namespace 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;
template<typename _Tp, typename _Up>
friend inline std::shared_ptr<_Tp> std::static_pointer_cast(const std::shared_ptr<_Up>& __r) noexcept;
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();
inline uint16_t client_id() { 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 */);
inline float get_volume() { 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 AudioCodec {
uint16_t last_packet_id = 0;
std::chrono::system_clock::time_point last_packet_timestamp;
bool successfully_initialized;
std::shared_ptr<audio::codec::Converter> converter;
std::shared_ptr<audio::AudioResampler> resampler;
};
std::array<std::unique_ptr<AudioCodec>, codec::MAX + 1> codec{
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
};
std::shared_ptr<audio::AudioOutputSource> output_source;
std::weak_ptr<VoiceClient> _ref;
v8::Persistent<v8::Object> _js_handle;
uint16_t _client_id;
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();
}
struct EncodedBuffer {
bool head;
uint16_t packet_id;
pipes::buffer buffer;
codec::value codec;
std::chrono::system_clock::time_point receive_timestamp;
};
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 process_encoded_buffer(const std::unique_ptr<EncodedBuffer>& /* buffer */);
};
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;
}
VoiceClientWrap(const std::shared_ptr<VoiceClient>&);
virtual ~VoiceClientWrap();
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();
};
}
}