608 lines
21 KiB
C++
608 lines
21 KiB
C++
|
|
#include "rtc_v8_bindings.h"
|
||
|
|
#include "rtc_bridge.h"
|
||
|
|
|
||
|
|
#include <kinc/log.h>
|
||
|
|
|
||
|
|
#include <algorithm>
|
||
|
|
#include <cstring>
|
||
|
|
#include <map>
|
||
|
|
#include <set>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
using namespace v8;
|
||
|
|
|
||
|
|
static Isolate *g_rtc_isolate = nullptr;
|
||
|
|
static std::map<int, Persistent<Function>> &g_desc_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_cand_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_state_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_gathering_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_dc_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_open_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_closed_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_error_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
static std::map<int, Persistent<Function>> &g_msg_cbs = *new std::map<int, Persistent<Function>>;
|
||
|
|
|
||
|
|
static std::map<int, std::vector<int>> &g_pc_dcs = *new std::map<int, std::vector<int>>;
|
||
|
|
static std::map<int, int> &g_dc_to_pc = *new std::map<int, int>;
|
||
|
|
static Global<Context> *g_rtc_context = nullptr;
|
||
|
|
|
||
|
|
static void rtc_throw(Isolate *isolate, const char *msg) {
|
||
|
|
isolate->ThrowException(
|
||
|
|
Exception::TypeError(String::NewFromUtf8(isolate, msg).ToLocalChecked()));
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool rtc_store_cb(const FunctionCallbackInfo<Value> &args,
|
||
|
|
std::map<int, Persistent<Function>> &cbs,
|
||
|
|
const char *name) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsFunction()) {
|
||
|
|
rtc_throw(isolate, name);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
int id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
cbs[id].Reset(isolate, args[1].As<Function>());
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void rtc_drop_cbs(int id) {
|
||
|
|
g_desc_cbs.erase(id);
|
||
|
|
g_cand_cbs.erase(id);
|
||
|
|
g_state_cbs.erase(id);
|
||
|
|
g_gathering_cbs.erase(id);
|
||
|
|
g_dc_cbs.erase(id);
|
||
|
|
g_open_cbs.erase(id);
|
||
|
|
g_closed_cbs.erase(id);
|
||
|
|
g_error_cbs.erase(id);
|
||
|
|
g_msg_cbs.erase(id);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void rtc_track_dc(int pc, int dc) {
|
||
|
|
g_pc_dcs[pc].push_back(dc);
|
||
|
|
g_dc_to_pc[dc] = pc;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void rtc_forget_dc(int dc) {
|
||
|
|
auto it = g_dc_to_pc.find(dc);
|
||
|
|
if (it == g_dc_to_pc.end()) return;
|
||
|
|
auto &dcs = g_pc_dcs[it->second];
|
||
|
|
dcs.erase(std::remove(dcs.begin(), dcs.end(), dc), dcs.end());
|
||
|
|
g_dc_to_pc.erase(it);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void rtc_call(Persistent<Function> &cb, int argc, Local<Value> *argv) {
|
||
|
|
if (cb.IsEmpty()) return;
|
||
|
|
Isolate *isolate = g_rtc_isolate;
|
||
|
|
Local<Context> ctx = isolate->GetCurrentContext();
|
||
|
|
Local<Function> fn = cb.Get(isolate);
|
||
|
|
TryCatch try_catch(isolate);
|
||
|
|
fn->Call(ctx, Null(isolate), argc, argv).IsEmpty();
|
||
|
|
if (try_catch.HasCaught()) {
|
||
|
|
String::Utf8Value err(isolate, try_catch.Exception());
|
||
|
|
kinc_log(KINC_LOG_LEVEL_WARNING, "runt_rtc callback threw: %s",
|
||
|
|
*err != nullptr ? *err : "unknown");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static Local<String> rtc_str(Isolate *isolate, const std::string &s) {
|
||
|
|
return String::NewFromUtf8(isolate, s.data(), NewStringType::kNormal,
|
||
|
|
(int)s.size())
|
||
|
|
.ToLocalChecked();
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_init(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
g_rtc_isolate = isolate;
|
||
|
|
args.GetReturnValue().Set(Integer::New(isolate, runt_rtc_init()));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_cleanup(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
runt_rtc_cleanup();
|
||
|
|
for (auto *m : {&g_desc_cbs, &g_cand_cbs, &g_state_cbs,
|
||
|
|
&g_gathering_cbs, &g_dc_cbs, &g_open_cbs,
|
||
|
|
&g_closed_cbs, &g_error_cbs, &g_msg_cbs}) {
|
||
|
|
for (auto &kv : *m) kv.second.Reset();
|
||
|
|
m->clear();
|
||
|
|
}
|
||
|
|
g_pc_dcs.clear();
|
||
|
|
g_dc_to_pc.clear();
|
||
|
|
RtcEvent ev;
|
||
|
|
while (runt_rtc_poll_event(ev)) {}
|
||
|
|
args.GetReturnValue().Set(Undefined(args.GetIsolate()));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_shutdown_all() {
|
||
|
|
std::set<int> dcs;
|
||
|
|
for (auto &kv : g_dc_to_pc) dcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_msg_cbs) dcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_open_cbs) dcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_closed_cbs) dcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_error_cbs) dcs.insert(kv.first);
|
||
|
|
for (int dc : dcs) runt_rtc_delete_data_channel(dc);
|
||
|
|
|
||
|
|
std::set<int> pcs;
|
||
|
|
for (auto &kv : g_pc_dcs) pcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_desc_cbs) pcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_cand_cbs) pcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_state_cbs) pcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_gathering_cbs) pcs.insert(kv.first);
|
||
|
|
for (auto &kv : g_dc_cbs) pcs.insert(kv.first);
|
||
|
|
for (int pc : pcs) runt_rtc_delete_peer_connection(pc);
|
||
|
|
|
||
|
|
for (auto *m : {&g_desc_cbs, &g_cand_cbs, &g_state_cbs,
|
||
|
|
&g_gathering_cbs, &g_dc_cbs, &g_open_cbs,
|
||
|
|
&g_closed_cbs, &g_error_cbs, &g_msg_cbs}) {
|
||
|
|
for (auto &kv : *m) kv.second.Reset();
|
||
|
|
m->clear();
|
||
|
|
}
|
||
|
|
g_pc_dcs.clear();
|
||
|
|
g_dc_to_pc.clear();
|
||
|
|
RtcEvent ev;
|
||
|
|
while (runt_rtc_poll_event(ev)) {}
|
||
|
|
runt_rtc_cleanup();
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_create_peer_connection(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
std::vector<std::string> iceServers;
|
||
|
|
if (args.Length() >= 1 && args[0]->IsArray()) {
|
||
|
|
Local<Array> arr = args[0].As<Array>();
|
||
|
|
Local<Context> ctx = isolate->GetCurrentContext();
|
||
|
|
for (uint32_t i = 0; i < arr->Length(); i++) {
|
||
|
|
Local<Value> el = arr->Get(ctx, i).ToLocalChecked();
|
||
|
|
if (el->IsString()) {
|
||
|
|
String::Utf8Value s(isolate, el);
|
||
|
|
iceServers.push_back(*s);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
int pc = runt_rtc_create_peer_connection(iceServers);
|
||
|
|
args.GetReturnValue().Set(Integer::New(isolate, pc));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_close_peer_connection(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int pc = args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
|
||
|
|
runt_rtc_close_peer_connection(pc);
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_delete_peer_connection(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int pc = args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
|
||
|
|
runt_rtc_delete_peer_connection(pc);
|
||
|
|
rtc_drop_cbs(pc);
|
||
|
|
auto it = g_pc_dcs.find(pc);
|
||
|
|
if (it != g_pc_dcs.end()) {
|
||
|
|
for (int dc : it->second) {
|
||
|
|
rtc_drop_cbs(dc);
|
||
|
|
g_dc_to_pc.erase(dc);
|
||
|
|
}
|
||
|
|
g_pc_dcs.erase(it);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_local_description(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int pc = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
const char *type = nullptr;
|
||
|
|
if (args.Length() >= 2 && args[1]->IsString()) {
|
||
|
|
String::Utf8Value typeStr(isolate, args[1]);
|
||
|
|
type = *typeStr;
|
||
|
|
}
|
||
|
|
runt_rtc_set_local_description(pc, type);
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_remote_description(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsString() ||
|
||
|
|
!args[2]->IsString()) {
|
||
|
|
rtc_throw(isolate,
|
||
|
|
"runt_rtc_set_remote_description requires (pc, sdp, type)");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
int pc = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
String::Utf8Value sdp(isolate, args[1]);
|
||
|
|
String::Utf8Value type(isolate, args[2]);
|
||
|
|
int ret = runt_rtc_set_remote_description(pc, *sdp, *type);
|
||
|
|
args.GetReturnValue().Set(Integer::New(isolate, ret));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_add_remote_candidate(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsString() ||
|
||
|
|
!args[2]->IsString()) {
|
||
|
|
rtc_throw(isolate,
|
||
|
|
"runt_rtc_add_remote_candidate requires (pc, cand, mid)");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
int pc = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
String::Utf8Value cand(isolate, args[1]);
|
||
|
|
String::Utf8Value mid(isolate, args[2]);
|
||
|
|
int ret = runt_rtc_add_remote_candidate(pc, *cand, *mid);
|
||
|
|
args.GetReturnValue().Set(Integer::New(isolate, ret));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_get_local_description(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int pc = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
args.GetReturnValue().Set(
|
||
|
|
rtc_str(isolate, runt_rtc_get_local_description(pc)));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_get_local_description_type(
|
||
|
|
const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int pc = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
args.GetReturnValue().Set(
|
||
|
|
rtc_str(isolate, runt_rtc_get_local_description_type(pc)));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_create_data_channel(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsString()) {
|
||
|
|
rtc_throw(isolate, "runt_rtc_create_data_channel requires (pc, label)");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
int pc = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
String::Utf8Value label(isolate, args[1]);
|
||
|
|
bool unordered = false;
|
||
|
|
int maxRetransmits = -1;
|
||
|
|
if (args.Length() >= 3 && args[2]->IsBoolean()) {
|
||
|
|
unordered = args[2]->BooleanValue(isolate);
|
||
|
|
}
|
||
|
|
if (args.Length() >= 4 && args[3]->IsNumber()) {
|
||
|
|
maxRetransmits =
|
||
|
|
args[3]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
}
|
||
|
|
int dc = runt_rtc_create_data_channel(pc, *label, unordered,
|
||
|
|
maxRetransmits);
|
||
|
|
if (dc >= 0) rtc_track_dc(pc, dc);
|
||
|
|
args.GetReturnValue().Set(Integer::New(isolate, dc));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_delete_data_channel(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int dc = args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
|
||
|
|
runt_rtc_delete_data_channel(dc);
|
||
|
|
rtc_drop_cbs(dc);
|
||
|
|
rtc_forget_dc(dc);
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_send_message(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 2 || !args[0]->IsNumber()) {
|
||
|
|
rtc_throw(isolate, "runt_rtc_send_message requires (id, data)");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
int id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
int ret = -1;
|
||
|
|
if (args[1]->IsArrayBuffer()) {
|
||
|
|
Local<ArrayBuffer> buf = args[1].As<ArrayBuffer>();
|
||
|
|
ret = runt_rtc_send_message(
|
||
|
|
id, static_cast<const char *>(buf->GetBackingStore()->Data()),
|
||
|
|
(int)buf->ByteLength());
|
||
|
|
} else if (args[1]->IsArrayBufferView()) {
|
||
|
|
Local<ArrayBufferView> view = args[1].As<ArrayBufferView>();
|
||
|
|
Local<ArrayBuffer> buf = view->Buffer();
|
||
|
|
const char *data =
|
||
|
|
static_cast<const char *>(buf->GetBackingStore()->Data()) +
|
||
|
|
view->ByteOffset();
|
||
|
|
ret = runt_rtc_send_message(id, data, (int)view->ByteLength());
|
||
|
|
} else if (args[1]->IsString()) {
|
||
|
|
String::Utf8Value s(isolate, args[1]);
|
||
|
|
ret = runt_rtc_send_message(id, *s, -1);
|
||
|
|
} else {
|
||
|
|
rtc_throw(isolate,
|
||
|
|
"runt_rtc_send_message data must be String or ArrayBuffer");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
args.GetReturnValue().Set(Integer::New(isolate, ret));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_close(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int id = args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
|
||
|
|
runt_rtc_close(id);
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_delete(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int id = args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
|
||
|
|
runt_rtc_delete(id);
|
||
|
|
rtc_drop_cbs(id);
|
||
|
|
rtc_forget_dc(id);
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_is_open(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
args.GetReturnValue().Set(Boolean::New(isolate, runt_rtc_is_open(id)));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_get_buffered_amount(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) return;
|
||
|
|
int id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
args.GetReturnValue().Set(
|
||
|
|
Integer::New(isolate, runt_rtc_get_buffered_amount(id)));
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_local_description_callback(
|
||
|
|
const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_desc_cbs,
|
||
|
|
"runt_rtc_set_local_description_callback requires (pc, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_local_candidate_callback(
|
||
|
|
const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_cand_cbs,
|
||
|
|
"runt_rtc_set_local_candidate_callback requires (pc, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_state_change_callback(
|
||
|
|
const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_state_cbs,
|
||
|
|
"runt_rtc_set_state_change_callback requires (pc, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_gathering_state_callback(
|
||
|
|
const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_gathering_cbs,
|
||
|
|
"runt_rtc_set_gathering_state_callback requires (pc, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_data_channel_callback(
|
||
|
|
const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_dc_cbs,
|
||
|
|
"runt_rtc_set_data_channel_callback requires (pc, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_open_callback(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_open_cbs,
|
||
|
|
"runt_rtc_set_open_callback requires (id, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_closed_callback(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_closed_cbs,
|
||
|
|
"runt_rtc_set_closed_callback requires (id, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_error_callback(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_error_cbs,
|
||
|
|
"runt_rtc_set_error_callback requires (id, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_set_message_callback(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
rtc_store_cb(args, g_msg_cbs,
|
||
|
|
"runt_rtc_set_message_callback requires (id, fn)");
|
||
|
|
}
|
||
|
|
|
||
|
|
void v8_runt_rtc_random_bytes(const FunctionCallbackInfo<Value> &args) {
|
||
|
|
Isolate *isolate = args.GetIsolate();
|
||
|
|
if (args.Length() < 1 || !args[0]->IsNumber()) {
|
||
|
|
rtc_throw(isolate, "runt_rtc_random_bytes requires (len)");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
int len = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||
|
|
if (len <= 0) {
|
||
|
|
args.GetReturnValue().Set(Null(isolate));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
Local<ArrayBuffer> buf = ArrayBuffer::New(isolate, len);
|
||
|
|
if (!runt_rtc_random_bytes(
|
||
|
|
static_cast<char *>(buf->GetBackingStore()->Data()), len)) {
|
||
|
|
args.GetReturnValue().Set(Null(isolate));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
args.GetReturnValue().Set(buf);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void rtc_drain_events() {
|
||
|
|
Isolate *isolate = g_rtc_isolate;
|
||
|
|
RtcEvent ev;
|
||
|
|
while (runt_rtc_poll_event(ev)) {
|
||
|
|
switch (ev.type) {
|
||
|
|
case RtcEventType::LocalDescription: {
|
||
|
|
auto it = g_desc_cbs.find(ev.id);
|
||
|
|
if (it != g_desc_cbs.end()) {
|
||
|
|
Local<Value> argv[2] = {rtc_str(isolate, ev.s1),
|
||
|
|
rtc_str(isolate, ev.s2)};
|
||
|
|
rtc_call(it->second, 2, argv);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::LocalCandidate: {
|
||
|
|
auto it = g_cand_cbs.find(ev.id);
|
||
|
|
if (it != g_cand_cbs.end()) {
|
||
|
|
Local<Value> argv[2] = {rtc_str(isolate, ev.s1),
|
||
|
|
rtc_str(isolate, ev.s2)};
|
||
|
|
rtc_call(it->second, 2, argv);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::StateChange: {
|
||
|
|
auto it = g_state_cbs.find(ev.id);
|
||
|
|
if (it != g_state_cbs.end()) {
|
||
|
|
Local<Value> argv[1] = {Integer::New(isolate, ev.arg)};
|
||
|
|
rtc_call(it->second, 1, argv);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::GatheringState: {
|
||
|
|
auto it = g_gathering_cbs.find(ev.id);
|
||
|
|
if (it != g_gathering_cbs.end()) {
|
||
|
|
Local<Value> argv[1] = {Integer::New(isolate, ev.arg)};
|
||
|
|
rtc_call(it->second, 1, argv);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::DataChannel: {
|
||
|
|
rtc_track_dc(ev.id, ev.arg);
|
||
|
|
auto it = g_dc_cbs.find(ev.id);
|
||
|
|
if (it != g_dc_cbs.end()) {
|
||
|
|
Local<Value> argv[1] = {Integer::New(isolate, ev.arg)};
|
||
|
|
rtc_call(it->second, 1, argv);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::Open: {
|
||
|
|
auto it = g_open_cbs.find(ev.id);
|
||
|
|
if (it != g_open_cbs.end()) {
|
||
|
|
rtc_call(it->second, 0, nullptr);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::Closed: {
|
||
|
|
auto it = g_closed_cbs.find(ev.id);
|
||
|
|
if (it != g_closed_cbs.end()) {
|
||
|
|
rtc_call(it->second, 0, nullptr);
|
||
|
|
}
|
||
|
|
rtc_drop_cbs(ev.id);
|
||
|
|
rtc_forget_dc(ev.id);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::Error: {
|
||
|
|
auto it = g_error_cbs.find(ev.id);
|
||
|
|
if (it != g_error_cbs.end()) {
|
||
|
|
Local<Value> argv[1] = {rtc_str(isolate, ev.s1)};
|
||
|
|
rtc_call(it->second, 1, argv);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case RtcEventType::Message: {
|
||
|
|
auto it = g_msg_cbs.find(ev.id);
|
||
|
|
if (it != g_msg_cbs.end()) {
|
||
|
|
Local<Value> arg;
|
||
|
|
if (ev.isString) {
|
||
|
|
arg = rtc_str(isolate, ev.s1);
|
||
|
|
} else {
|
||
|
|
// move the payload into the backing store (no copy)
|
||
|
|
auto *payload = new std::vector<char>(std::move(ev.data));
|
||
|
|
auto backing = ArrayBuffer::NewBackingStore(
|
||
|
|
payload->data(), payload->size(),
|
||
|
|
[](void *, size_t, void *deleter_data) {
|
||
|
|
delete static_cast<std::vector<char> *>(deleter_data);
|
||
|
|
},
|
||
|
|
payload);
|
||
|
|
arg = ArrayBuffer::New(isolate, std::move(backing));
|
||
|
|
}
|
||
|
|
Local<Value> argv[1] = {arg};
|
||
|
|
rtc_call(it->second, 1, argv);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void runt_rtc_process_events_native() {
|
||
|
|
if (g_rtc_isolate == nullptr || g_rtc_context == nullptr) return;
|
||
|
|
Locker locker{g_rtc_isolate};
|
||
|
|
Isolate::Scope isolate_scope(g_rtc_isolate);
|
||
|
|
HandleScope handle_scope(g_rtc_isolate);
|
||
|
|
Local<Context> ctx = Local<Context>::New(g_rtc_isolate, *g_rtc_context);
|
||
|
|
Context::Scope context_scope(ctx);
|
||
|
|
rtc_drain_events();
|
||
|
|
}
|
||
|
|
|
||
|
|
void bind_rtc_bridge(Isolate *isolate, const Global<Context> &context) {
|
||
|
|
Locker locker{isolate};
|
||
|
|
Isolate::Scope isolate_scope(isolate);
|
||
|
|
HandleScope handle_scope(isolate);
|
||
|
|
|
||
|
|
Local<Context> current_context = Local<Context>::New(isolate, context);
|
||
|
|
Context::Scope context_scope(current_context);
|
||
|
|
Local<Object> global = current_context->Global();
|
||
|
|
|
||
|
|
g_rtc_isolate = isolate;
|
||
|
|
if (g_rtc_context == nullptr) {
|
||
|
|
g_rtc_context = new Global<Context>(isolate, context);
|
||
|
|
}
|
||
|
|
|
||
|
|
Local<Object> runt_obj;
|
||
|
|
Local<Value> existing_runt =
|
||
|
|
global->Get(current_context,
|
||
|
|
String::NewFromUtf8(isolate, "RunT").ToLocalChecked())
|
||
|
|
.ToLocalChecked();
|
||
|
|
if (existing_runt->IsObject()) {
|
||
|
|
runt_obj = existing_runt->ToObject(current_context).ToLocalChecked();
|
||
|
|
} else {
|
||
|
|
runt_obj = Object::New(isolate);
|
||
|
|
global->Set(current_context,
|
||
|
|
String::NewFromUtf8(isolate, "RunT").ToLocalChecked(),
|
||
|
|
runt_obj);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct RtcBinding {
|
||
|
|
const char *global_name;
|
||
|
|
const char *runt_name;
|
||
|
|
FunctionCallback fn;
|
||
|
|
};
|
||
|
|
static const RtcBinding bindings[] = {
|
||
|
|
{"runt_rtc_init", "rtcInit", v8_runt_rtc_init},
|
||
|
|
{"runt_rtc_cleanup", "rtcCleanup", v8_runt_rtc_cleanup},
|
||
|
|
{"runt_rtc_create_peer_connection", "rtcCreatePeerConnection",
|
||
|
|
v8_runt_rtc_create_peer_connection},
|
||
|
|
{"runt_rtc_close_peer_connection", "rtcClosePeerConnection",
|
||
|
|
v8_runt_rtc_close_peer_connection},
|
||
|
|
{"runt_rtc_delete_peer_connection", "rtcDeletePeerConnection",
|
||
|
|
v8_runt_rtc_delete_peer_connection},
|
||
|
|
{"runt_rtc_set_local_description", "rtcSetLocalDescription",
|
||
|
|
v8_runt_rtc_set_local_description},
|
||
|
|
{"runt_rtc_set_remote_description", "rtcSetRemoteDescription",
|
||
|
|
v8_runt_rtc_set_remote_description},
|
||
|
|
{"runt_rtc_add_remote_candidate", "rtcAddRemoteCandidate",
|
||
|
|
v8_runt_rtc_add_remote_candidate},
|
||
|
|
{"runt_rtc_get_local_description", "rtcGetLocalDescription",
|
||
|
|
v8_runt_rtc_get_local_description},
|
||
|
|
{"runt_rtc_get_local_description_type", "rtcGetLocalDescriptionType",
|
||
|
|
v8_runt_rtc_get_local_description_type},
|
||
|
|
{"runt_rtc_create_data_channel", "rtcCreateDataChannel",
|
||
|
|
v8_runt_rtc_create_data_channel},
|
||
|
|
{"runt_rtc_delete_data_channel", "rtcDeleteDataChannel",
|
||
|
|
v8_runt_rtc_delete_data_channel},
|
||
|
|
{"runt_rtc_send_message", "rtcSendMessage", v8_runt_rtc_send_message},
|
||
|
|
{"runt_rtc_close", "rtcClose", v8_runt_rtc_close},
|
||
|
|
{"runt_rtc_delete", "rtcDelete", v8_runt_rtc_delete},
|
||
|
|
{"runt_rtc_is_open", "rtcIsOpen", v8_runt_rtc_is_open},
|
||
|
|
{"runt_rtc_get_buffered_amount", "rtcGetBufferedAmount",
|
||
|
|
v8_runt_rtc_get_buffered_amount},
|
||
|
|
{"runt_rtc_set_local_description_callback",
|
||
|
|
"rtcSetLocalDescriptionCallback",
|
||
|
|
v8_runt_rtc_set_local_description_callback},
|
||
|
|
{"runt_rtc_set_local_candidate_callback",
|
||
|
|
"rtcSetLocalCandidateCallback",
|
||
|
|
v8_runt_rtc_set_local_candidate_callback},
|
||
|
|
{"runt_rtc_set_state_change_callback", "rtcSetStateChangeCallback",
|
||
|
|
v8_runt_rtc_set_state_change_callback},
|
||
|
|
{"runt_rtc_set_gathering_state_callback", "rtcSetGatheringStateCallback",
|
||
|
|
v8_runt_rtc_set_gathering_state_callback},
|
||
|
|
{"runt_rtc_set_data_channel_callback", "rtcSetDataChannelCallback",
|
||
|
|
v8_runt_rtc_set_data_channel_callback},
|
||
|
|
{"runt_rtc_set_open_callback", "rtcSetOpenCallback",
|
||
|
|
v8_runt_rtc_set_open_callback},
|
||
|
|
{"runt_rtc_set_closed_callback", "rtcSetClosedCallback",
|
||
|
|
v8_runt_rtc_set_closed_callback},
|
||
|
|
{"runt_rtc_set_error_callback", "rtcSetErrorCallback",
|
||
|
|
v8_runt_rtc_set_error_callback},
|
||
|
|
{"runt_rtc_set_message_callback", "rtcSetMessageCallback",
|
||
|
|
v8_runt_rtc_set_message_callback},
|
||
|
|
{"runt_rtc_random_bytes", "rtcRandomBytes",
|
||
|
|
v8_runt_rtc_random_bytes},
|
||
|
|
};
|
||
|
|
|
||
|
|
for (const RtcBinding &b : bindings) {
|
||
|
|
Local<Function> fn =
|
||
|
|
Function::New(current_context, b.fn).ToLocalChecked();
|
||
|
|
global->Set(current_context,
|
||
|
|
String::NewFromUtf8(isolate, b.global_name).ToLocalChecked(),
|
||
|
|
fn);
|
||
|
|
runt_obj->Set(current_context,
|
||
|
|
String::NewFromUtf8(isolate, b.runt_name).ToLocalChecked(),
|
||
|
|
fn);
|
||
|
|
}
|
||
|
|
}
|