TeaSpeak-Client/native/codec/codec/NativeCodec.cpp

205 lines
6.4 KiB
C++

#include <chrono>
#include <thread>
#include <memory>
#include "NativeCodec.h"
#include "OpusCodec.h"
#include "SpeexCodec.h"
#include "CeltCodec.h"
#include "NanException.h"
#include <iostream>
using namespace std;
using namespace std::chrono;
using namespace tc;
using namespace v8;
using namespace Nan;
#define DEFINE_ENUM_ENTRY(name, value) \
Nan::ForceSet(types, Nan::New<v8::String>(name).ToLocalChecked(), Nan::New<v8::Number>(value), static_cast<PropertyAttribute>(ReadOnly|DontDelete)); \
//Nan::ForceSet(types, Nan::New<v8::Number>(value), Nan::New<v8::String>(name).ToLocalChecked(), static_cast<PropertyAttribute>(ReadOnly|DontDelete));
NAN_MODULE_INIT(NativeCodec::CodecType::Init) {
auto types = Nan::New<v8::Object>();
DEFINE_ENUM_ENTRY("OPUS_VOICE", CodecType::OPUS_VOICE);
DEFINE_ENUM_ENTRY("OPUS_MUSIC", CodecType::OPUS_MUSIC);
DEFINE_ENUM_ENTRY("SPEEX_NARROWBAND", CodecType::SPEEX_NARROWBAND);
DEFINE_ENUM_ENTRY("SPEEX_WIDEBAND", CodecType::SPEEX_WIDEBAND);
DEFINE_ENUM_ENTRY("SPEEX_ULTRA_WIDEBAND", CodecType::SPEEX_ULTRA_WIDEBAND);
DEFINE_ENUM_ENTRY("CELT_MONO", CodecType::CELT_MONO);
Nan::Set(target, Nan::New<v8::String>("CodecTypes").ToLocalChecked(), types);
Nan::Set(target, Nan::New<v8::String>("codec_supported").ToLocalChecked(), Nan::New<v8::Function>(NativeCodec::CodecType::supported));
}
NAN_METHOD(NativeCodec::CodecType::supported) {
if(!info[0]->IsNumber()) {
NAN_THROW_EXCEPTION(Error, "Argument 0 shall be a number!");
return;
}
auto type = (CodecType::value) Nan::To<int>(info[0]).FromJust();
if(type == CodecType::OPUS_MUSIC || type == CodecType::OPUS_VOICE) {
info.GetReturnValue().Set(OpusCodec::supported());
} else if(type == CodecType::SPEEX_NARROWBAND || type == CodecType::SPEEX_WIDEBAND || type == CodecType::SPEEX_ULTRA_WIDEBAND) {
info.GetReturnValue().Set(SpeexCodec::supported());
} else if(type == CodecType::CELT_MONO) {
info.GetReturnValue().Set(CeltCodec::supported());
} else {
NAN_THROW_EXCEPTION(Error, "Invalid type!");
return;
}
}
NAN_MODULE_INIT(NativeCodec::Init) {
auto klass = New<FunctionTemplate>(NewInstance);
klass->SetClassName(Nan::New("NativeCodec").ToLocalChecked());
klass->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(klass, "decode", NativeCodec::function_decode);
Nan::SetPrototypeMethod(klass, "encode", NativeCodec::function_encode);
Nan::SetPrototypeMethod(klass, "initialize", NativeCodec::function_initialize);
Nan::SetPrototypeMethod(klass, "finalize", NativeCodec::function_finalize);
constructor().Reset(Nan::GetFunction(klass).ToLocalChecked());
//Nan::Set(target, Nan::New("NativeCodec").ToLocalChecked(), Nan::GetFunction(klass).ToLocalChecked());
}
NAN_METHOD(NativeCodec::NewInstance) {
if (info.IsConstructCall()) {
if(!info[0]->IsNumber()) {
NAN_THROW_EXCEPTION(Error, "Argument 0 shall be a number!");
return;
}
auto type = (CodecType::value) Nan::To<int>(info[0]).FromJust();
std::unique_ptr<NativeCodec> instance;
if(type == CodecType::OPUS_MUSIC || type == CodecType::OPUS_VOICE) {
#ifdef HAVE_OPUS
instance = make_unique<OpusCodec>(type);
#endif
} else if(type == CodecType::SPEEX_NARROWBAND || type == CodecType::SPEEX_WIDEBAND || type == CodecType::SPEEX_ULTRA_WIDEBAND) {
#ifdef HAVE_SPEEX
instance = make_unique<SpeexCodec>(type);
#endif
} else if(type == CodecType::CELT_MONO) {
#ifdef HAVE_CELT
instance = make_unique<CeltCodec>(type);
#endif
} else {
NAN_THROW_EXCEPTION(Error, "Invalid type!");
return;
}
if(!instance) {
NAN_THROW_EXCEPTION(Error, "Target codec isn't supported!");
return;
}
instance.release()->Wrap(info.This());
Nan::Set(info.This(), New<String>("type").ToLocalChecked(), New<Number>(type));
info.GetReturnValue().Set(info.This());
} else {
const int argc = 1;
v8::Local<v8::Value> argv[argc] = {info[0]};
v8::Local<v8::Function> cons = Nan::New(constructor());
Nan::TryCatch try_catch;
auto result = Nan::NewInstance(cons, argc, argv);
if(try_catch.HasCaught()) {
try_catch.ReThrow();
return;
}
info.GetReturnValue().Set(result.ToLocalChecked());
}
}
NAN_METHOD(NativeCodec::function_decode) {
if(info.Length() != 3) {
NAN_THROW_EXCEPTION(Error, "Invalid argument count!");
return;
}
if(!info[0]->IsArrayBuffer() || !info[1]->IsFunction() || !info[2]->IsFunction()) {
NAN_THROW_EXCEPTION(Error, "Invalid argument types!");
return;
}
auto codec = ObjectWrap::Unwrap<NativeCodec>(info.Holder());
codec->decode(info);
}
NAN_METHOD(NativeCodec::function_encode) {
if(info.Length() != 3) {
NAN_THROW_EXCEPTION(Error, "Invalid argument count!");
return;
}
if(!info[0]->IsArrayBuffer() || !info[1]->IsFunction() || !info[2]->IsFunction()) {
NAN_THROW_EXCEPTION(Error, "Invalid argument types!");
return;
}
auto codec = ObjectWrap::Unwrap<NativeCodec>(info.Holder());
codec->encode(info);
}
NAN_METHOD(NativeCodec::function_initialize) {
auto codec = ObjectWrap::Unwrap<NativeCodec>(info.Holder());
codec->initialize(info);
}
NAN_METHOD(NativeCodec::function_finalize) {
auto codec = ObjectWrap::Unwrap<NativeCodec>(info.Holder());
codec->finalize(info);
}
NativeCodec::NativeCodec(tc::NativeCodec::CodecType::value type) : type(type) {}
NativeCodec::~NativeCodec() {}
WorkerPool::WorkerPool() {}
WorkerPool::~WorkerPool() {}
void WorkerPool::initialize() {
assert(!this->_running);
this->_running = true;
this->worker = thread([&]{
while(this->_running) {
function<void()> worker;
{
unique_lock lock(this->worker_lock);
this->worker_wait.wait_for(lock, minutes(1), [&]{ return !this->_running || !this->tasks.empty(); });
if(!this->_running) break;
if(this->tasks.empty()) continue;
worker = move(this->tasks.front());
this->tasks.pop_front();
}
try {
worker();
} catch(std::exception& ex) {
cerr << "failed to invoke opus worker! message: " << ex.what() << endl;
} catch (...) {
cerr << "failed to invoke opus worker!" << endl;
}
}
});
#ifndef WIN32
auto worker_handle = this->worker.native_handle();
pthread_setname_np(worker_handle, "Codec Worker");
#endif
}
void WorkerPool::finalize() {
this->_running = false;
this->worker_wait.notify_all();
this->tasks.clear();
if(this->worker.joinable())
this->worker.join();
}
void WorkerPool::enqueue_task(std::function<void()> task) {
lock_guard lock(this->worker_lock);
this->tasks.push_back(std::move(task));
this->worker_wait.notify_one();
}