Skip to content

Commit dd1e8fb

Browse files
authored
Merge pull request #1518 from streamlabs/transition-events
Implementing transition events
2 parents 19ccebc + c346892 commit dd1e8fb

File tree

4 files changed

+234
-59
lines changed

4 files changed

+234
-59
lines changed

obs-studio-client/source/callback-manager.cpp

Lines changed: 104 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ bool globalCallback::worker_stop = true;
3535
uint32_t globalCallback::sleepIntervalMS = 50;
3636
std::thread *globalCallback::worker_thread = nullptr;
3737
Napi::ThreadSafeFunction globalCallback::js_source_callback;
38+
Napi::ThreadSafeFunction globalCallback::js_transition_callback;
3839
Napi::ThreadSafeFunction globalCallback::js_volmeter_callback;
3940
bool globalCallback::m_all_workers_stop = false;
4041
std::mutex globalCallback::mtx_volmeters;
@@ -45,6 +46,9 @@ void globalCallback::Init(Napi::Env env, Napi::Object exports)
4546
exports.Set(Napi::String::New(env, "RegisterSourceCallback"), Napi::Function::New(env, globalCallback::RegisterSourceCallback));
4647
exports.Set(Napi::String::New(env, "RemoveSourceCallback"), Napi::Function::New(env, globalCallback::RemoveSourceCallback));
4748

49+
exports.Set(Napi::String::New(env, "RegisterTransitionCallback"), Napi::Function::New(env, globalCallback::RegisterTransitionCallback));
50+
exports.Set(Napi::String::New(env, "RemoveTransitionCallback"), Napi::Function::New(env, globalCallback::RemoveTransitionCallback));
51+
4852
exports.Set(Napi::String::New(env, "RegisterVolmeterCallback"), Napi::Function::New(env, globalCallback::RegisterVolmeterCallback));
4953
exports.Set(Napi::String::New(env, "RemoveVolmeterCallback"), Napi::Function::New(env, globalCallback::RemoveVolmeterCallback));
5054
}
@@ -70,6 +74,21 @@ Napi::Value globalCallback::RemoveSourceCallback(const Napi::CallbackInfo &info)
7074
return info.Env().Undefined();
7175
}
7276

77+
Napi::Value globalCallback::RegisterTransitionCallback(const Napi::CallbackInfo &info)
78+
{
79+
Napi::Function async_callback = info[0].As<Napi::Function>();
80+
js_transition_callback = Napi::ThreadSafeFunction::New(info.Env(), async_callback, "TransitionCallback", 0, 1, [](Napi::Env) {});
81+
82+
return Napi::Boolean::New(info.Env(), true);
83+
}
84+
85+
Napi::Value globalCallback::RemoveTransitionCallback(const Napi::CallbackInfo &info)
86+
{
87+
js_transition_callback.Release();
88+
89+
return info.Env().Undefined();
90+
}
91+
7392
Napi::Value globalCallback::RegisterVolmeterCallback(const Napi::CallbackInfo &info)
7493
{
7594
Napi::Function async_callback = info[0].As<Napi::Function>();
@@ -124,6 +143,36 @@ void globalCallback::worker()
124143
delete data;
125144
};
126145

146+
auto transitions_callback = [](Napi::Env env, Napi::Function jsCallback, TransitionInfoData *data) {
147+
try {
148+
Napi::Array result = Napi::Array::New(env, data->items.size());
149+
150+
for (size_t i = 0; i < data->items.size(); i++) {
151+
Napi::Object obj = Napi::Object::New(env);
152+
obj.Set("id", Napi::String::New(env, data->items[i]->id));
153+
154+
switch (data->items[i]->event) {
155+
case TransitionInfo::START:
156+
obj.Set("event", Napi::String::New(env, "start"));
157+
break;
158+
159+
case TransitionInfo::STOP:
160+
obj.Set("event", Napi::String::New(env, "stop"));
161+
break;
162+
163+
default:
164+
// do nothing
165+
break;
166+
}
167+
168+
result.Set(static_cast<uint32_t>(i), obj);
169+
}
170+
jsCallback.Call({result});
171+
} catch (...) {
172+
}
173+
delete data;
174+
};
175+
127176
auto volmeter_callback = [](Napi::Env env, Napi::Function jsCallback, VolmeterDataArray *dataArray) {
128177
try {
129178
Napi::Array result = Napi::Array::New(env, dataArray->items.size());
@@ -183,66 +232,82 @@ void globalCallback::worker()
183232
}
184233
}
185234

186-
{
187-
std::vector<ipc::value> response = conn->call_synchronous_helper(
188-
"CallbackManager", "GlobalQuery", {ipc::value((uint64_t)volmeters_ids.size()), ipc::value(volmeters_ids)});
189-
if (!response.size() || (response.size() == 1)) {
190-
goto do_sleep;
191-
}
235+
std::vector<ipc::value> response = conn->call_synchronous_helper("CallbackManager", "GlobalQuery",
236+
{ipc::value((uint64_t)volmeters_ids.size()), ipc::value(volmeters_ids)});
237+
if (!response.size() || (response.size() == 1)) {
238+
goto do_sleep;
239+
}
192240

193-
uint32_t index = 1;
241+
uint32_t index = 1;
194242

243+
const auto sourcesSize = response[index++].value_union.ui32;
244+
if (sourcesSize) {
195245
SourceSizeInfoData *data = new SourceSizeInfoData{{}};
196-
for (uint32_t i = 2; i < (response[1].value_union.ui32 * 4) + 2; i++) {
246+
for (uint32_t i = 0; i < sourcesSize; i++) {
197247
SourceSizeInfo *item = new SourceSizeInfo;
198248

199-
item->name = response[i++].value_str;
200-
item->width = response[i++].value_union.ui32;
201-
item->height = response[i++].value_union.ui32;
202-
item->flags = response[i].value_union.ui32;
249+
item->name = response[index++].value_str;
250+
item->width = response[index++].value_union.ui32;
251+
item->height = response[index++].value_union.ui32;
252+
item->flags = response[index++].value_union.ui32;
253+
data->items.emplace_back(item);
254+
}
255+
256+
napi_status status = js_source_callback.NonBlockingCall(data, sources_callback);
257+
if (status != napi_ok) {
258+
delete data;
259+
}
260+
}
261+
262+
const auto transitionsSize = response[index++].value_union.ui32;
263+
if (transitionsSize) {
264+
TransitionInfoData *data = new TransitionInfoData{{}};
265+
for (uint32_t i = 0; i < transitionsSize; i++) {
266+
TransitionInfo *item = new TransitionInfo;
267+
268+
item->id = response[index++].value_str;
269+
item->event = static_cast<TransitionInfo::EventType>(response[index++].value_union.ui32);
270+
203271
data->items.emplace_back(item);
204-
index = i;
205272
}
206273

207274
if (data->items.size() > 0) {
208-
napi_status status = js_source_callback.NonBlockingCall(data, sources_callback);
275+
napi_status status = js_transition_callback.NonBlockingCall(data, transitions_callback);
209276
if (status != napi_ok) {
210277
delete data;
211278
}
212279
}
280+
}
213281

214-
index++;
215-
216-
auto volmeterDataArray = new VolmeterDataArray;
217-
while (volmeters_size--) {
218-
VolmeterData *item = new VolmeterData{{}, {}, {}};
282+
auto volmeterDataArray = new VolmeterDataArray;
283+
while (volmeters_size--) {
284+
VolmeterData *item = new VolmeterData{{}, {}, {}};
219285

220-
item->source_name = response[index++].value_str;
286+
item->source_name = response[index++].value_str;
221287

222-
size_t channels = response[index++].value_union.i32;
223-
bool isMuted = response[index++].value_union.i32;
288+
size_t channels = response[index++].value_union.i32;
289+
bool isMuted = response[index++].value_union.i32;
224290

225-
if (!isMuted) {
226-
item->magnitude.resize(channels);
227-
item->peak.resize(channels);
228-
item->input_peak.resize(channels);
229-
for (size_t ch = 0; ch < channels; ch++) {
230-
item->magnitude[ch] = response[index + ch * 3 + 0].value_union.fp32;
231-
item->peak[ch] = response[index + ch * 3 + 1].value_union.fp32;
232-
item->input_peak[ch] = response[index + ch * 3 + 2].value_union.fp32;
233-
}
291+
if (!isMuted) {
292+
item->magnitude.resize(channels);
293+
item->peak.resize(channels);
294+
item->input_peak.resize(channels);
295+
for (size_t ch = 0; ch < channels; ch++) {
296+
item->magnitude[ch] = response[index + ch * 3 + 0].value_union.fp32;
297+
item->peak[ch] = response[index + ch * 3 + 1].value_union.fp32;
298+
item->input_peak[ch] = response[index + ch * 3 + 2].value_union.fp32;
299+
}
234300

235-
index += static_cast<uint32_t>((3 * channels));
301+
index += static_cast<uint32_t>((3 * channels));
236302

237-
volmeterDataArray->items.emplace_back(item);
238-
}
303+
volmeterDataArray->items.emplace_back(item);
239304
}
305+
}
240306

241-
if (js_volmeter_callback) {
242-
napi_status status = js_volmeter_callback.NonBlockingCall(volmeterDataArray, volmeter_callback);
243-
if (status != napi_ok) {
244-
delete volmeterDataArray;
245-
}
307+
if (js_volmeter_callback) {
308+
napi_status status = js_volmeter_callback.NonBlockingCall(volmeterDataArray, volmeter_callback);
309+
if (status != napi_ok) {
310+
delete volmeterDataArray;
246311
}
247312
}
248313

obs-studio-client/source/callback-manager.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <mutex>
2020
#include <napi.h>
21+
#include <string>
2122
#include <thread>
2223
#include <unordered_set>
2324
#include "utility-v8.hpp"
@@ -33,12 +34,27 @@ struct SourceSizeInfoData {
3334
std::vector<std::unique_ptr<SourceSizeInfo>> items;
3435
};
3536

37+
struct TransitionInfo {
38+
enum EventType : uint32_t {
39+
START = 0,
40+
STOP = 1,
41+
};
42+
43+
std::string id;
44+
EventType event;
45+
};
46+
47+
struct TransitionInfoData {
48+
std::vector<std::unique_ptr<TransitionInfo>> items;
49+
};
50+
3651
namespace globalCallback {
3752
extern bool isWorkerRunning;
3853
extern bool worker_stop;
3954
extern uint32_t sleepIntervalMS;
4055
extern std::thread *worker_thread;
4156
extern Napi::ThreadSafeFunction js_source_callback;
57+
extern Napi::ThreadSafeFunction js_transition_callback;
4258
extern Napi::ThreadSafeFunction js_volmeter_callback;
4359
extern bool m_all_workers_stop;
4460

@@ -57,6 +73,9 @@ void Init(Napi::Env env, Napi::Object exports);
5773
Napi::Value RegisterSourceCallback(const Napi::CallbackInfo &info);
5874
Napi::Value RemoveSourceCallback(const Napi::CallbackInfo &info);
5975

76+
Napi::Value RegisterTransitionCallback(const Napi::CallbackInfo &info);
77+
Napi::Value RemoveTransitionCallback(const Napi::CallbackInfo &info);
78+
6079
Napi::Value RegisterVolmeterCallback(const Napi::CallbackInfo &info);
6180
Napi::Value RemoveVolmeterCallback(const Napi::CallbackInfo &info);
6281
}

0 commit comments

Comments
 (0)