Skip to content

Commit a2cbb04

Browse files
author
eddyStreamlabs
committed
Merge branch 'staging' of github.com:stream-labs/obs-studio-node into crash-handler-integration
2 parents 8e0ffb4 + ce7f4a3 commit a2cbb04

30 files changed

+1870
-1712
lines changed

js/module.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,8 +660,8 @@ export interface IAudioFactory {
660660
reset(info: IAudioInfo): boolean;
661661
getGlobal(): IAudio;
662662
}
663-
export interface IModuleFactory {
664-
create(binPath: string, dataPath: string): IModule;
663+
export interface IModuleFactory extends IFactoryTypes {
664+
open(binPath: string, dataPath: string): IModule;
665665
loadAll(): void;
666666
addPath(path: string, dataPath: string): void;
667667
logLoaded(): void;

js/module.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ export const enum EDeinterlaceFieldOrder {
5050
Bottom
5151
}
5252

53+
export const enum EVideoCodes {
54+
Success = 0,
55+
Fail = -1,
56+
NotSupported = -2,
57+
InvalidParam = -3,
58+
CurrentlyActive = -4,
59+
ModuleNotFound = -5
60+
}
61+
5362
export const enum EDeinterlaceMode {
5463
Disable,
5564
Discard,
@@ -1493,8 +1502,8 @@ export interface IAudioFactory {
14931502
}
14941503

14951504

1496-
export interface IModuleFactory {
1497-
create(binPath: string, dataPath: string): IModule;
1505+
export interface IModuleFactory extends IFactoryTypes {
1506+
open(binPath: string, dataPath: string): IModule;
14981507
loadAll(): void;
14991508
addPath(path: string, dataPath: string): void;
15001509
logLoaded(): void;

lib-streamlabs-ipc

obs-studio-client/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ SET(PROJECT_SOURCE
6969
"${PROJECT_SOURCE_DIR}/source/main.cpp"
7070
"${PROJECT_SOURCE_DIR}/source/volmeter.cpp" "${PROJECT_SOURCE_DIR}/source/volmeter.hpp"
7171
"${PROJECT_SOURCE_DIR}/source/video.cpp" "${PROJECT_SOURCE_DIR}/source/video.hpp"
72+
"${PROJECT_SOURCE_DIR}/source/module.cpp" "${PROJECT_SOURCE_DIR}/source/module.hpp"
7273
)
7374
SET(PROJECT_LIBRARIES
7475
${NODEJS_LIBRARIES}

obs-studio-client/source/isource.cpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "isource.hpp"
1919
#include <error.hpp>
20+
#include <functional>
2021
#include "controller.hpp"
2122
#include "obs-property.hpp"
2223
#include "properties.hpp"
@@ -25,6 +26,84 @@
2526
#include "utility.hpp"
2627

2728
Nan::Persistent<v8::FunctionTemplate> osn::ISource::prototype = Nan::Persistent<v8::FunctionTemplate>();
29+
osn::ISource* sourceObject;
30+
31+
osn::ISource::~ISource()
32+
{
33+
stop_worker();
34+
stop_async_runner();
35+
}
36+
37+
void osn::ISource::start_async_runner()
38+
{
39+
if (m_async_callback)
40+
return;
41+
42+
std::unique_lock<std::mutex> ul(m_worker_lock);
43+
44+
// Start v8/uv asynchronous runner.
45+
m_async_callback = new osn::SourceCallback();
46+
m_async_callback->set_handler(
47+
std::bind(&ISource::callback_handler, this, std::placeholders::_1, std::placeholders::_2), nullptr);
48+
}
49+
50+
void osn::ISource::stop_async_runner()
51+
{
52+
if (!m_async_callback)
53+
return;
54+
55+
std::unique_lock<std::mutex> ul(m_worker_lock);
56+
57+
// Stop v8/uv asynchronous runner.
58+
m_async_callback->clear();
59+
m_async_callback->finalize();
60+
m_async_callback = nullptr;
61+
}
62+
63+
void osn::ISource::callback_handler(void* data, std::shared_ptr<std::vector<SourceHotkeyInfo>> item)
64+
{
65+
v8::Isolate* isolate = v8::Isolate::GetCurrent();
66+
v8::Local<v8::Value> args[1];
67+
68+
for (auto& hotkeyInfo : *item) {
69+
v8::Local<v8::Value> argv = v8::Object::New(isolate);
70+
71+
argv->ToObject()->Set(
72+
v8::String::NewFromUtf8(isolate, "sourceName"),
73+
v8::String::NewFromUtf8(isolate, hotkeyInfo.sourceName.c_str()));
74+
argv->ToObject()->Set(
75+
v8::String::NewFromUtf8(isolate, "hotkeyName"),
76+
v8::String::NewFromUtf8(isolate, hotkeyInfo.hotkeyName.c_str()));
77+
argv->ToObject()->Set(
78+
v8::String::NewFromUtf8(isolate, "hotkeyDescription"),
79+
v8::String::NewFromUtf8(isolate, hotkeyInfo.hotkeyDesc.c_str()));
80+
argv->ToObject()->Set(
81+
v8::String::NewFromUtf8(isolate, "hotkeyId"), v8::Number::New(isolate, hotkeyInfo.hotkeyId));
82+
args[0] = argv;
83+
84+
Nan::Call(m_callback_function, 1, args);
85+
}
86+
}
87+
88+
void osn::ISource::start_worker()
89+
{
90+
if (!m_worker_stop)
91+
return;
92+
// Launch worker thread.
93+
m_worker_stop = false;
94+
m_worker = std::thread(std::bind(&osn::ISource::worker, this));
95+
}
96+
97+
void osn::ISource::stop_worker()
98+
{
99+
if (m_worker_stop != false)
100+
return;
101+
// Stop worker thread.
102+
m_worker_stop = true;
103+
if (m_worker.joinable()) {
104+
m_worker.join();
105+
}
106+
}
28107

29108
void osn::ISource::Register(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target)
30109
{
@@ -289,6 +368,105 @@ Nan::NAN_METHOD_RETURN_TYPE osn::ISource::GetSettings(Nan::NAN_METHOD_ARGS_TYPE
289368
return;
290369
}
291370

371+
Nan::NAN_METHOD_RETURN_TYPE osn::ISource::ConnectHotkeyCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
372+
{
373+
v8::Local<v8::Function> callback;
374+
ASSERT_GET_VALUE(args[0], callback);
375+
376+
// Grab IPC Connection
377+
std::shared_ptr<ipc::client> conn = nullptr;
378+
if (!(conn = GetConnection())) {
379+
return;
380+
}
381+
382+
// Callback
383+
sourceObject = new ISource();
384+
sourceObject->m_callback_function.Reset(callback);
385+
sourceObject->start_async_runner();
386+
sourceObject->set_keepalive(args.This());
387+
sourceObject->start_worker();
388+
args.GetReturnValue().Set(true);
389+
}
390+
391+
Nan::NAN_METHOD_RETURN_TYPE osn::ISource::DisconnectHotkeyCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
392+
{
393+
sourceObject->stop_worker();
394+
sourceObject->stop_async_runner();
395+
}
396+
397+
Nan::NAN_METHOD_RETURN_TYPE osn::ISource::ProcessHotkeyStatus(const v8::FunctionCallbackInfo<v8::Value>& args)
398+
{
399+
uint64_t hotkeyId;
400+
bool press;
401+
std::string sourceId;
402+
403+
ASSERT_GET_VALUE(args[0], hotkeyId);
404+
ASSERT_GET_VALUE(args[1], press);
405+
ASSERT_GET_VALUE(args[2], sourceId);
406+
407+
auto conn = GetConnection();
408+
if (!conn)
409+
return;
410+
411+
std::vector<ipc::value> response = conn->call_synchronous_helper(
412+
"Source", "ProcessHotkeyStatus", {ipc::value(hotkeyId), ipc::value(press), ipc::value(sourceId)});
413+
414+
if (!ValidateResponse(response))
415+
return;
416+
}
417+
418+
void osn::ISource::worker()
419+
{
420+
size_t totalSleepMS = 0;
421+
422+
while (!m_worker_stop) {
423+
auto tp_start = std::chrono::high_resolution_clock::now();
424+
425+
// Grab IPC Connection
426+
std::shared_ptr<ipc::client> conn = nullptr;
427+
if (!(conn = GetConnection())) {
428+
goto do_sleep;
429+
}
430+
431+
// Call
432+
{
433+
std::vector<ipc::value> response = conn->call_synchronous_helper("Source", "Query", {});
434+
if (!response.size() || (response.size() == 1)) {
435+
goto do_sleep;
436+
}
437+
438+
ErrorCode error = (ErrorCode)response[0].value_union.ui64;
439+
if (error == ErrorCode::Ok && (response.size() - 1) % 4 == 0) /* Each entry has 4 results */ {
440+
std::shared_ptr<std::vector<SourceHotkeyInfo>> data = std::make_shared<std::vector<SourceHotkeyInfo>>();
441+
442+
// For each hotkey pair
443+
for (int i = 1; i < response.size(); i += 4) {
444+
data->push_back({response[i].value_str,
445+
response[i + 1].value_str,
446+
response[i + 2].value_str,
447+
response[i + 3].value_union.ui64});
448+
}
449+
450+
m_async_callback->queue(std::move(data));
451+
}
452+
}
453+
454+
do_sleep:
455+
auto tp_end = std::chrono::high_resolution_clock::now();
456+
auto dur = std::chrono::duration_cast<std::chrono::milliseconds>(tp_end - tp_start);
457+
totalSleepMS = m_sleep_interval - dur.count();
458+
std::this_thread::sleep_for(std::chrono::milliseconds(totalSleepMS));
459+
}
460+
return;
461+
}
462+
463+
void osn::ISource::set_keepalive(v8::Local<v8::Object> obj)
464+
{
465+
if (!m_async_callback)
466+
return;
467+
m_async_callback->set_keepalive(obj);
468+
}
469+
292470
Nan::NAN_METHOD_RETURN_TYPE osn::ISource::Update(Nan::NAN_METHOD_ARGS_TYPE info)
293471
{
294472
v8::Local<v8::Object> json;
@@ -600,3 +778,16 @@ Nan::NAN_METHOD_RETURN_TYPE osn::ISource::SetEnabled(Nan::NAN_METHOD_ARGS_TYPE i
600778

601779
info.GetReturnValue().Set((bool)response[1].value_union.i32 == enabled);
602780
}
781+
782+
INITIALIZER(nodeobs_source)
783+
{
784+
initializerFunctions.push([](v8::Local<v8::Object> exports) {
785+
NODE_SET_METHOD(exports, "ConnectHotkeyCallback", osn::ISource::ConnectHotkeyCallback);
786+
});
787+
initializerFunctions.push([](v8::Local<v8::Object> exports) {
788+
NODE_SET_METHOD(exports, "DisconnectHotkeyCallback", osn::ISource::DisconnectHotkeyCallback);
789+
});
790+
initializerFunctions.push([](v8::Local<v8::Object> exports) {
791+
NODE_SET_METHOD(exports, "ProcessHotkeyStatus", osn::ISource::ProcessHotkeyStatus);
792+
});
793+
}

obs-studio-client/source/isource.hpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@
2222

2323
namespace osn
2424
{
25+
struct SourceHotkeyInfo
26+
{
27+
std::string sourceName;
28+
std::string hotkeyName;
29+
std::string hotkeyDesc;
30+
size_t hotkeyId;
31+
};
32+
33+
typedef utilv8::managed_callback<std::shared_ptr<std::vector<SourceHotkeyInfo>>> SourceCallback;
34+
2535
class ISource : public Nan::ObjectWrap, public utilv8::InterfaceObject<osn::ISource>
2636
{
2737
friend class utilv8::InterfaceObject<osn::ISource>;
@@ -31,7 +41,28 @@ namespace osn
3141

3242
public:
3343
uint64_t sourceId;
34-
virtual ~ISource(){};
44+
virtual ~ISource();
45+
46+
private:
47+
uint64_t m_uid;
48+
uint32_t m_sleep_interval = 33;
49+
50+
std::thread m_worker;
51+
bool m_worker_stop = true;
52+
std::mutex m_worker_lock;
53+
54+
osn::SourceCallback* m_async_callback = nullptr;
55+
Nan::Callback m_callback_function;
56+
57+
private:
58+
void start_async_runner();
59+
void stop_async_runner();
60+
void callback_handler(void* data, std::shared_ptr<std::vector<SourceHotkeyInfo>> item);
61+
62+
void start_worker();
63+
void stop_worker();
64+
void worker();
65+
void set_keepalive(v8::Local<v8::Object>);
3566

3667
public:
3768
static void Register(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
@@ -58,5 +89,9 @@ namespace osn
5889
static Nan::NAN_METHOD_RETURN_TYPE SetMuted(Nan::NAN_METHOD_ARGS_TYPE info);
5990
static Nan::NAN_METHOD_RETURN_TYPE GetEnabled(Nan::NAN_METHOD_ARGS_TYPE info);
6091
static Nan::NAN_METHOD_RETURN_TYPE SetEnabled(Nan::NAN_METHOD_ARGS_TYPE info);
92+
93+
static void ConnectHotkeyCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
94+
static void DisconnectHotkeyCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
95+
static void ProcessHotkeyStatus(const v8::FunctionCallbackInfo<v8::Value>& args);
6196
};
6297
} // namespace osn

obs-studio-client/source/main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "transition.hpp"
3030
#include "video.hpp"
3131
#include "volmeter.hpp"
32+
#include "module.hpp"
3233

3334
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1;
3435

@@ -47,6 +48,7 @@ void main(v8::Local<v8::Object> exports, v8::Local<v8::Value> module, void* priv
4748
osn::Fader::Register(exports);
4849
osn::VolMeter::Register(exports);
4950
osn::Video::Register(exports);
51+
osn::Module::Register(exports);
5052

5153
while (initializerFunctions.size() > 0) {
5254
initializerFunctions.front()(exports);

0 commit comments

Comments
 (0)