noobs 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/COPYING +339 -0
  2. package/README.md +46 -0
  3. package/bin/64bit/obs.lib +0 -0
  4. package/binding.gyp +23 -0
  5. package/dist/bin/Qt6Core.dll +0 -0
  6. package/dist/bin/Qt6Gui.dll +0 -0
  7. package/dist/bin/Qt6Network.dll +0 -0
  8. package/dist/bin/Qt6Svg.dll +0 -0
  9. package/dist/bin/Qt6Widgets.dll +0 -0
  10. package/dist/bin/Qt6Xml.dll +0 -0
  11. package/dist/bin/avcodec-61.dll +0 -0
  12. package/dist/bin/avdevice-61.dll +0 -0
  13. package/dist/bin/avfilter-10.dll +0 -0
  14. package/dist/bin/avformat-61.dll +0 -0
  15. package/dist/bin/avutil-59.dll +0 -0
  16. package/dist/bin/datachannel.dll +0 -0
  17. package/dist/bin/libcurl.dll +0 -0
  18. package/dist/bin/libobs-d3d11.dll +0 -0
  19. package/dist/bin/libobs-opengl.dll +0 -0
  20. package/dist/bin/libobs-winrt.dll +0 -0
  21. package/dist/bin/librist.dll +0 -0
  22. package/dist/bin/libx264-164.dll +0 -0
  23. package/dist/bin/lua51.dll +0 -0
  24. package/dist/bin/obs-amf-test.exe +0 -0
  25. package/dist/bin/obs-ffmpeg-mux.exe +0 -0
  26. package/dist/bin/obs-frontend-api.dll +0 -0
  27. package/dist/bin/obs-scripting.dll +0 -0
  28. package/dist/bin/obs.dll +0 -0
  29. package/dist/bin/srt.dll +0 -0
  30. package/dist/bin/swresample-5.dll +0 -0
  31. package/dist/bin/swscale-8.dll +0 -0
  32. package/dist/bin/w32-pthreads.dll +0 -0
  33. package/dist/bin/zlib.dll +0 -0
  34. package/dist/effects/area.effect +250 -0
  35. package/dist/effects/bicubic_scale.effect +236 -0
  36. package/dist/effects/bilinear_lowres_scale.effect +123 -0
  37. package/dist/effects/color.effect +172 -0
  38. package/dist/effects/default.effect +254 -0
  39. package/dist/effects/default_rect.effect +84 -0
  40. package/dist/effects/deinterlace_base.effect +325 -0
  41. package/dist/effects/deinterlace_blend.effect +21 -0
  42. package/dist/effects/deinterlace_blend_2x.effect +21 -0
  43. package/dist/effects/deinterlace_discard.effect +21 -0
  44. package/dist/effects/deinterlace_discard_2x.effect +21 -0
  45. package/dist/effects/deinterlace_linear.effect +21 -0
  46. package/dist/effects/deinterlace_linear_2x.effect +21 -0
  47. package/dist/effects/deinterlace_yadif.effect +21 -0
  48. package/dist/effects/deinterlace_yadif_2x.effect +21 -0
  49. package/dist/effects/format_conversion.effect +1823 -0
  50. package/dist/effects/lanczos_scale.effect +292 -0
  51. package/dist/effects/opaque.effect +159 -0
  52. package/dist/effects/premultiplied_alpha.effect +38 -0
  53. package/dist/effects/repeat.effect +36 -0
  54. package/dist/effects/solid.effect +80 -0
  55. package/dist/noobs.node +0 -0
  56. package/dist/plugins/obs-ffmpeg.dll +0 -0
  57. package/dist/plugins/obs-x264.dll +0 -0
  58. package/dist/plugins/win-capture.dll +0 -0
  59. package/include/audio-monitoring/osx/mac-helpers.h +13 -0
  60. package/include/audio-monitoring/pulse/pulseaudio-wrapper.h +212 -0
  61. package/include/audio-monitoring/win32/wasapi-output.h +22 -0
  62. package/include/callback/calldata.h +195 -0
  63. package/include/callback/decl.h +61 -0
  64. package/include/callback/proc.h +52 -0
  65. package/include/callback/signal.h +73 -0
  66. package/include/graphics/axisang.h +65 -0
  67. package/include/graphics/bounds.h +108 -0
  68. package/include/graphics/device-exports.h +177 -0
  69. package/include/graphics/effect-parser.h +290 -0
  70. package/include/graphics/effect.h +190 -0
  71. package/include/graphics/graphics-internal.h +335 -0
  72. package/include/graphics/graphics.h +1024 -0
  73. package/include/graphics/half.h +100 -0
  74. package/include/graphics/image-file.h +124 -0
  75. package/include/graphics/input.h +34 -0
  76. package/include/graphics/libnsgif/libnsgif.h +142 -0
  77. package/include/graphics/math-defs.h +45 -0
  78. package/include/graphics/math-extra.h +61 -0
  79. package/include/graphics/matrix3.h +98 -0
  80. package/include/graphics/matrix4.h +102 -0
  81. package/include/graphics/plane.h +85 -0
  82. package/include/graphics/quat.h +170 -0
  83. package/include/graphics/shader-parser.h +273 -0
  84. package/include/graphics/srgb.h +177 -0
  85. package/include/graphics/vec2.h +148 -0
  86. package/include/graphics/vec3.h +224 -0
  87. package/include/graphics/vec4.h +241 -0
  88. package/include/media-io/audio-io.h +228 -0
  89. package/include/media-io/audio-math.h +43 -0
  90. package/include/media-io/audio-resampler.h +44 -0
  91. package/include/media-io/format-conversion.h +50 -0
  92. package/include/media-io/frame-rate.h +29 -0
  93. package/include/media-io/media-io-defs.h +20 -0
  94. package/include/media-io/media-remux.h +37 -0
  95. package/include/media-io/video-frame.h +64 -0
  96. package/include/media-io/video-io.h +338 -0
  97. package/include/media-io/video-scaler.h +43 -0
  98. package/include/obs-audio-controls.h +250 -0
  99. package/include/obs-av1.h +47 -0
  100. package/include/obs-avc.h +55 -0
  101. package/include/obs-config.h +52 -0
  102. package/include/obs-data.h +311 -0
  103. package/include/obs-defs.h +52 -0
  104. package/include/obs-encoder.h +361 -0
  105. package/include/obs-ffmpeg-compat.h +13 -0
  106. package/include/obs-hevc.h +81 -0
  107. package/include/obs-hotkey.h +271 -0
  108. package/include/obs-hotkeys.h +653 -0
  109. package/include/obs-interaction.h +56 -0
  110. package/include/obs-internal.h +1459 -0
  111. package/include/obs-missing-files.h +53 -0
  112. package/include/obs-module.h +181 -0
  113. package/include/obs-nal.h +37 -0
  114. package/include/obs-nix-platform.h +53 -0
  115. package/include/obs-nix-wayland.h +24 -0
  116. package/include/obs-nix-x11.h +22 -0
  117. package/include/obs-nix.h +42 -0
  118. package/include/obs-output.h +96 -0
  119. package/include/obs-properties.h +364 -0
  120. package/include/obs-scene.h +127 -0
  121. package/include/obs-service.h +115 -0
  122. package/include/obs-source.h +568 -0
  123. package/include/obs.h +2608 -0
  124. package/include/obsconfig.h +13 -0
  125. package/include/obsversion.h +5 -0
  126. package/include/util/apple/cfstring-utils.h +16 -0
  127. package/include/util/array-serializer.h +37 -0
  128. package/include/util/base.h +97 -0
  129. package/include/util/bitstream.h +28 -0
  130. package/include/util/bmem.h +94 -0
  131. package/include/util/buffered-file-serializer.h +32 -0
  132. package/include/util/c99defs.h +75 -0
  133. package/include/util/cf-lexer.h +199 -0
  134. package/include/util/cf-parser.h +281 -0
  135. package/include/util/circlebuf.h +319 -0
  136. package/include/util/config-file.h +103 -0
  137. package/include/util/crc32.h +29 -0
  138. package/include/util/curl/curl-helper.h +35 -0
  139. package/include/util/darray.h +606 -0
  140. package/include/util/deque.h +319 -0
  141. package/include/util/dstr.h +320 -0
  142. package/include/util/file-serializer.h +34 -0
  143. package/include/util/lexer.h +273 -0
  144. package/include/util/pipe.h +52 -0
  145. package/include/util/platform.h +223 -0
  146. package/include/util/profiler.h +97 -0
  147. package/include/util/serializer.h +158 -0
  148. package/include/util/simde/check.h +285 -0
  149. package/include/util/simde/debug-trap.h +117 -0
  150. package/include/util/simde/hedley.h +2123 -0
  151. package/include/util/simde/simde-align.h +481 -0
  152. package/include/util/simde/simde-arch.h +537 -0
  153. package/include/util/simde/simde-common.h +918 -0
  154. package/include/util/simde/simde-constify.h +925 -0
  155. package/include/util/simde/simde-detect-clang.h +114 -0
  156. package/include/util/simde/simde-diagnostic.h +447 -0
  157. package/include/util/simde/simde-features.h +550 -0
  158. package/include/util/simde/simde-math.h +1858 -0
  159. package/include/util/simde/x86/mmx.h +2456 -0
  160. package/include/util/simde/x86/sse.h +4479 -0
  161. package/include/util/simde/x86/sse2.h +7549 -0
  162. package/include/util/source-profiler.h +66 -0
  163. package/include/util/sse-intrin.h +32 -0
  164. package/include/util/task.h +22 -0
  165. package/include/util/text-lookup.h +45 -0
  166. package/include/util/threading-posix.h +77 -0
  167. package/include/util/threading-windows.h +142 -0
  168. package/include/util/threading.h +103 -0
  169. package/include/util/utf8.h +35 -0
  170. package/include/util/uthash.h +34 -0
  171. package/include/util/util_uint128.h +108 -0
  172. package/include/util/util_uint64.h +34 -0
  173. package/include/util/windows/device-enum.h +14 -0
  174. package/include/util/windows/obfuscate.h +16 -0
  175. package/include/util/windows/win-registry.h +37 -0
  176. package/include/util/windows/win-version.h +57 -0
  177. package/include/util/windows/window-helpers.h +47 -0
  178. package/index.d.ts +38 -0
  179. package/index.js +8 -0
  180. package/package.json +31 -0
  181. package/src/main.cpp +321 -0
  182. package/src/obs_interface.cpp +605 -0
  183. package/src/obs_interface.h +74 -0
  184. package/src/utils.cpp +80 -0
  185. package/src/utils.h +3 -0
@@ -0,0 +1,605 @@
1
+ #include <iostream>
2
+ #include <windows.h>
3
+ #include <obs.h>
4
+ #include "utils.h"
5
+ #include <chrono>
6
+ #include "obs_interface.h"
7
+ #include <vector>
8
+ #include <thread>
9
+
10
+
11
+
12
+ std::vector<std::string> ObsInterface::get_available_video_encoders()
13
+ {
14
+ std::vector<std::string> encoders;
15
+ size_t idx = 0;
16
+ const char *encoder_type;
17
+
18
+ while (obs_enum_encoder_types(idx++, &encoder_type)) {
19
+ bool video = obs_get_encoder_type(encoder_type) == OBS_ENCODER_VIDEO;
20
+
21
+ if (video)
22
+ encoders.emplace_back(encoder_type);
23
+ }
24
+
25
+ return encoders;
26
+ }
27
+
28
+ void ObsInterface::list_encoders(obs_encoder_type type)
29
+ {
30
+ blog(LOG_INFO, "List encoders");
31
+ blog(LOG_INFO, "List encoders of type: %d", type);
32
+
33
+ size_t idx = 0;
34
+ const char *encoder_type;
35
+
36
+ while (obs_enum_encoder_types(idx++, &encoder_type)) {
37
+ if (obs_get_encoder_type(encoder_type) != type) {
38
+ continue;
39
+ }
40
+
41
+ // if (obs_get_encoder_caps(encoder_type) & hide_flags) {
42
+ // continue;
43
+ // }
44
+
45
+ blog(LOG_INFO, "\t- %s (%s)", encoder_type, obs_encoder_get_display_name(encoder_type));
46
+ }
47
+ };
48
+
49
+ void ObsInterface::list_source_types()
50
+ {
51
+ blog(LOG_INFO, "List src types");
52
+ size_t idx = 0;
53
+ const char *src = nullptr;
54
+
55
+ while (obs_enum_source_types(idx++, &src)) {
56
+ blog(LOG_INFO, "\t- %s", src);
57
+ }
58
+ }
59
+
60
+ void ObsInterface::list_input_types()
61
+ {
62
+ blog(LOG_INFO, "List input types");
63
+ size_t idx = 0;
64
+ const char *src = nullptr;
65
+
66
+ while (obs_enum_input_types(idx++, &src)) {
67
+ blog(LOG_INFO, "\t- %s", src);
68
+ }
69
+ }
70
+
71
+ void ObsInterface::list_output_types()
72
+ {
73
+ blog(LOG_INFO, "List output types");
74
+ size_t idx = 0;
75
+ const char *src = nullptr;
76
+
77
+ while (obs_enum_output_types(idx++, &src)) {
78
+ blog(LOG_INFO, "\t- %s", src);
79
+ }
80
+ }
81
+
82
+ void ObsInterface::load_module(const char* module) {
83
+ blog(LOG_INFO, "Loading module: %s", module);
84
+
85
+ obs_module_t *ptr = NULL;
86
+ int success = obs_open_module(&ptr, module, NULL);
87
+
88
+ if (success != MODULE_SUCCESS) {
89
+ blog(LOG_ERROR, "Failed to open module: %s", module);
90
+ throw std::runtime_error("Failed to open module!");
91
+ }
92
+
93
+ bool initmod = obs_init_module(ptr);
94
+
95
+ if (!initmod) {
96
+ blog(LOG_ERROR, "Failed to initialize module!");
97
+ throw std::runtime_error("Module initialization failed!");
98
+ }
99
+ }
100
+
101
+ void ObsInterface::reset_video() {
102
+ blog(LOG_INFO, "Setup video info");
103
+
104
+ obs_video_info ovi = {};
105
+
106
+ ovi.base_width = 1920;
107
+ ovi.base_height = 1080;
108
+ ovi.output_width = 1920;
109
+ ovi.output_height = 1080;
110
+ ovi.fps_num = 60;
111
+ ovi.fps_den = 1;
112
+
113
+ ovi.output_format = VIDEO_FORMAT_NV12;
114
+ ovi.colorspace = VIDEO_CS_DEFAULT;
115
+ ovi.range = VIDEO_RANGE_DEFAULT;
116
+ ovi.scale_type = OBS_SCALE_BILINEAR;
117
+ ovi.adapter = 0;
118
+ ovi.gpu_conversion = true;
119
+ ovi.graphics_module = "libobs-d3d11.dll";
120
+
121
+ int success = obs_reset_video(&ovi);
122
+
123
+ if (success != OBS_VIDEO_SUCCESS) {
124
+ blog(LOG_ERROR, "Failed to reset video!");
125
+ throw std::runtime_error("Failed to reset video!");
126
+ }
127
+ }
128
+
129
+ void ObsInterface::reset_audio() {
130
+ struct obs_audio_info oai = {0};
131
+ oai.samples_per_sec = 48000;
132
+ oai.speakers = SPEAKERS_STEREO;
133
+ bool reset = obs_reset_audio(&oai);
134
+
135
+ if (!reset) {
136
+ blog(LOG_ERROR, "Failed to reset audio!");
137
+ throw std::runtime_error("Failed to reset audio!");
138
+ }
139
+ }
140
+
141
+ void ObsInterface::init_obs(const std::string& pluginPath, const std::string& dataPath) {
142
+ blog(LOG_INFO, "Enter init_obs");
143
+ auto success = obs_startup("en-US", NULL, NULL);
144
+
145
+ if (!success) {
146
+ blog(LOG_ERROR, "Failed to start OBS!");
147
+ throw std::runtime_error("OBS startup failed");
148
+ }
149
+
150
+ if (!obs_initialized()) {
151
+ blog(LOG_ERROR, "OBS not initialized!");
152
+ throw std::runtime_error("OBS initialization failed");
153
+ }
154
+
155
+ std::string dp = dataPath;
156
+
157
+ if (dp.back() != '/' && dp.back() != '\\') {
158
+ // Add a trailing slash if not present, else libobs gets upset.
159
+ dp += '/';
160
+ }
161
+
162
+ obs_add_data_path(dp.c_str()); // This is deprecated in libobs but it works for now.
163
+ std::vector<std::string> modules = { "obs-x264.dll", "obs-ffmpeg.dll", "win-capture.dll" };
164
+
165
+ for (const auto& module : modules) {
166
+ std::string path = pluginPath + "/" + module;
167
+ load_module(path.c_str());
168
+ }
169
+
170
+ obs_post_load_modules();
171
+
172
+ list_encoders();
173
+ list_source_types();
174
+ list_input_types();
175
+ list_output_types();
176
+
177
+ reset_video();
178
+ reset_audio();
179
+
180
+ blog(LOG_INFO, "Exit init_obs");
181
+ }
182
+
183
+ obs_output_t* ObsInterface::create_output(const std::string& recordingPath) {
184
+ blog(LOG_INFO, "Create output");
185
+ obs_output_t *output = obs_output_create("replay_buffer", "recording_output", NULL, NULL);
186
+
187
+ if (!output) {
188
+ blog(LOG_ERROR, "Failed to create output!");
189
+ throw std::runtime_error("Failed to create output!");
190
+ }
191
+
192
+ blog(LOG_INFO, "Set output settings");
193
+ obs_data_t *settings = obs_data_create();
194
+ obs_data_set_int(settings, "max_time_sec", 60);
195
+ obs_data_set_int(settings, "max_size_mb", 1024);
196
+ obs_data_set_string(settings, "directory", recordingPath.c_str());
197
+ obs_data_set_string(settings, "format", "%CCYY-%MM-%DD %hh-%mm-%ss");
198
+ obs_data_set_string(settings, "extension", "mp4");
199
+ obs_output_update(output, settings);
200
+ obs_data_release(settings);
201
+
202
+ blog(LOG_INFO, "Create venc");
203
+ video_encoder = obs_video_encoder_create("h264_texture_amf", "simple_h264_stream", NULL, NULL);
204
+
205
+ if (!video_encoder) {
206
+ blog(LOG_ERROR, "Failed to create video encoder!");
207
+ throw std::runtime_error("Failed to create video encoder!");
208
+ }
209
+
210
+ blog(LOG_INFO, "Set video encoder settings");
211
+ obs_data_t* amf_settings = obs_data_create();
212
+ obs_data_set_string(amf_settings, "preset", "speed"); // Faster preset
213
+ //obs_data_set_int(amf_settings, "bitrate", 2500);
214
+ obs_data_set_string(amf_settings, "rate_control", "CQP");
215
+ obs_data_set_int(amf_settings, "cqp", 30);
216
+ obs_data_set_string(amf_settings, "profile", "main");
217
+ obs_data_set_int(amf_settings, "keyint_sec", 1); // Set keyframe interval to 1 second
218
+ obs_encoder_update(video_encoder, amf_settings);
219
+ obs_data_release(amf_settings);
220
+
221
+ blog(LOG_INFO, "Create aenc");
222
+ audio_encoder = obs_audio_encoder_create("ffmpeg_aac", "simple_aac", NULL, 0, NULL);
223
+
224
+ if (!audio_encoder) {
225
+ blog(LOG_ERROR, "Failed to create audio encoder!");
226
+ throw std::runtime_error("Failed to create audio encoder!");
227
+ }
228
+
229
+ blog(LOG_INFO, "Set audio encoder settings");
230
+ obs_data_t *aenc_settings = obs_data_create();
231
+ obs_data_set_int(aenc_settings, "bitrate", 128);
232
+ obs_encoder_update(audio_encoder, aenc_settings);
233
+ obs_data_release(aenc_settings);
234
+
235
+ obs_output_set_video_encoder(output, video_encoder);
236
+ obs_output_set_audio_encoder(output, audio_encoder, 0);
237
+
238
+ obs_encoder_set_video(video_encoder, obs_get_video());
239
+ obs_encoder_set_audio(audio_encoder, obs_get_audio());
240
+
241
+ return output;
242
+ }
243
+
244
+ obs_scene_t* ObsInterface::create_scene() {
245
+ blog(LOG_INFO, "Create scene and src");
246
+ obs_scene_t *scene = obs_scene_create("WCR Scene");
247
+
248
+ if (!scene)
249
+ throw std::runtime_error("Failed to create scene!");
250
+
251
+ obs_source_t *scene_source = obs_scene_get_source(scene);
252
+
253
+ if (!scene_source)
254
+ throw std::runtime_error("Failed to get scene src!");
255
+
256
+ obs_set_output_source(0, scene_source); // 0 = video track
257
+ return scene;
258
+ }
259
+
260
+ obs_source_t* ObsInterface::create_video_source() {
261
+ blog(LOG_INFO, "Create display capture source");
262
+ // Create settings for monitor capture
263
+ obs_data_t *monitor_settings = obs_data_create();
264
+ obs_data_set_int(monitor_settings, "monitor", 0); // Monitor 0
265
+ obs_data_set_bool(monitor_settings, "capture_cursor", true);
266
+
267
+ obs_source_t *source = obs_source_create("monitor_capture", "Monitor", monitor_settings, NULL);
268
+ obs_data_release(monitor_settings);
269
+
270
+ if (!source)
271
+ throw std::runtime_error("Failed to create video source!");
272
+
273
+ return source;
274
+ }
275
+
276
+ void call_jscb(Napi::Env env, Napi::Function cb, SignalData* sd) {
277
+ Napi::Object obj = Napi::Object::New(env);
278
+ obj.Set("id", Napi::String::New(env, sd->id));
279
+ obj.Set("code", Napi::Number::New(env, sd->code));
280
+ cb.Call({ obj });
281
+ delete sd;
282
+ }
283
+
284
+ void ObsInterface::output_signal_handler_starting(void *data, calldata_t *cd) {
285
+ long long code = calldata_int(cd, "code");
286
+ ObsInterface* self = static_cast<ObsInterface*>(data);
287
+ SignalData* sd = new SignalData{ "starting", code };
288
+ self->jscb.NonBlockingCall(sd, call_jscb);
289
+ }
290
+
291
+ void ObsInterface::output_signal_handler_start(void *data, calldata_t *cd) {
292
+ long long code = calldata_int(cd, "code");
293
+ ObsInterface* self = static_cast<ObsInterface*>(data);
294
+ SignalData* sd = new SignalData{ "start", code };
295
+ self->jscb.NonBlockingCall(sd, call_jscb);
296
+ }
297
+
298
+ void ObsInterface::output_signal_handler_stop(void *data, calldata_t *cd) {
299
+ long long code = calldata_int(cd, "code");
300
+ ObsInterface* self = static_cast<ObsInterface*>(data);
301
+ SignalData* sd = new SignalData{ "stop", code };
302
+ self->jscb.NonBlockingCall(sd, call_jscb);
303
+ }
304
+
305
+ void ObsInterface::output_signal_handler_stopping(void *data, calldata_t *cd) {
306
+ long long code = calldata_int(cd, "code");
307
+ ObsInterface* self = static_cast<ObsInterface*>(data);
308
+ SignalData* sd = new SignalData{ "stopping", code };
309
+ self->jscb.NonBlockingCall(sd, call_jscb);
310
+ }
311
+
312
+ void ObsInterface::output_signal_handler_saved(void *data, calldata_t *cd) {
313
+ long long code = calldata_int(cd, "code");
314
+ ObsInterface* self = static_cast<ObsInterface*>(data);
315
+ SignalData* sd = new SignalData{ "saved", code };
316
+ self->jscb.NonBlockingCall(sd, call_jscb);
317
+ }
318
+
319
+ void ObsInterface::create_signal_handlers(obs_output_t *output) {
320
+ signal_handler_t *sh = obs_output_get_signal_handler(output);
321
+ signal_handler_connect(sh, "starting", output_signal_handler_starting, this);
322
+ signal_handler_connect(sh, "start", output_signal_handler_start, this);
323
+ signal_handler_connect(sh, "stopping", output_signal_handler_stopping, this);
324
+ signal_handler_connect(sh, "stop", output_signal_handler_stop, this);
325
+ signal_handler_connect(sh, "saved", output_signal_handler_saved, this);
326
+ }
327
+
328
+ void draw_callback(void* data, uint32_t cx, uint32_t cy) {
329
+ // Render the OBS preview scene here
330
+ obs_render_main_texture();
331
+ }
332
+
333
+ void ObsInterface::showPreview(HWND hwnd) {
334
+ blog(LOG_INFO, "ObsInterface::showPreview");
335
+
336
+ if (display) {
337
+ blog(LOG_INFO, "Display already exists, returning early");
338
+ return; // Return early if display already exists
339
+ }
340
+
341
+ // Create an embedded child window for OBS preview
342
+ HWND previewWindow = CreateWindowExA(
343
+ 0, // No extended styles
344
+ "STATIC", // Simple static control class (ANSI string)
345
+ "OBS Preview", // Window name (ANSI string)
346
+ WS_CHILD | WS_VISIBLE | WS_BORDER, // Child + visible + border
347
+ 20, 20, // Position within parent (x, y)
348
+ 1920, 1080, // Size (width, height)
349
+ hwnd, // Parent window (your Electron app)
350
+ NULL, // No menu
351
+ GetModuleHandle(NULL),
352
+ NULL
353
+ );
354
+
355
+ if (!previewWindow) {
356
+ blog(LOG_ERROR, "Failed to create preview child window");
357
+ return;
358
+ }
359
+
360
+ // Store for cleanup
361
+ previewHwnd = previewWindow;
362
+
363
+ blog(LOG_INFO, "Create OBS display in child window");
364
+ gs_init_data gs_data = {};
365
+ gs_data.adapter = 0;
366
+ gs_data.cx = 1920; // Match child window size
367
+ gs_data.cy = 1080;
368
+ gs_data.format = GS_BGRA;
369
+ gs_data.zsformat = GS_ZS_NONE;
370
+ gs_data.num_backbuffers = 1;
371
+ gs_data.window.hwnd = previewWindow; // Use child window, not parent
372
+
373
+ display = obs_display_create(&gs_data, 0x0);
374
+ if (display) {
375
+ obs_display_add_draw_callback(display, draw_callback, NULL);
376
+ blog(LOG_INFO, "OBS preview embedded successfully");
377
+ } else {
378
+ blog(LOG_ERROR, "Failed to create OBS display");
379
+ DestroyWindow(previewWindow);
380
+ previewHwnd = nullptr;
381
+ }
382
+ }
383
+
384
+ void ObsInterface::resizePreview(int width, int height) {
385
+ blog(LOG_INFO, "ObsInterface::resizePreview to size (%d x %d)", width, height);
386
+
387
+ if (!previewHwnd) {
388
+ blog(LOG_WARNING, "No preview window to resize");
389
+ return;
390
+ }
391
+
392
+ // Resize the child window
393
+ BOOL windowSuccess = SetWindowPos(
394
+ previewHwnd, // Handle to the child window
395
+ NULL, // No Z-order change
396
+ 0, 0, // Keep current position (ignored due to SWP_NOMOVE)
397
+ width, height, // New size (width, height)
398
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE // Don't change position, Z-order, or activation
399
+ );
400
+
401
+ if (!windowSuccess) {
402
+ blog(LOG_ERROR, "Failed to resize preview window to (%d x %d)", width, height);
403
+ return;
404
+ }
405
+
406
+ blog(LOG_INFO, "Preview window resized successfully to (%d x %d)", width, height);
407
+
408
+ // Resize the OBS display to match the new window size
409
+ if (display) {
410
+ obs_display_resize(display, width, height);
411
+ blog(LOG_INFO, "OBS display resized to (%d x %d)", width, height);
412
+ } else {
413
+ blog(LOG_WARNING, "No OBS display to resize");
414
+ }
415
+ }
416
+
417
+ void ObsInterface::movePreview(int x, int y) {
418
+ blog(LOG_INFO, "ObsInterface::movePreview to position (%d, %d)", x, y);
419
+
420
+ if (!previewHwnd) {
421
+ blog(LOG_WARNING, "No preview window to move");
422
+ return;
423
+ }
424
+
425
+ // Move the child window to the new position within the parent
426
+ BOOL success = SetWindowPos(
427
+ previewHwnd, // Handle to the child window
428
+ NULL, // No Z-order change
429
+ x, y, // New position (x, y)
430
+ 0, 0, // Keep current size (ignored due to SWP_NOSIZE)
431
+ SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE // Don't change size, Z-order, or activation
432
+ );
433
+
434
+ if (success) {
435
+ blog(LOG_INFO, "Preview window moved successfully to (%d, %d)", x, y);
436
+ } else {
437
+ blog(LOG_ERROR, "Failed to move preview window to (%d, %d)", x, y);
438
+ }
439
+ }
440
+
441
+ void ObsInterface::hidePreview() {
442
+ blog(LOG_INFO, "ObsInterface::hidePreview");
443
+
444
+ if (display) {
445
+ obs_display_remove_draw_callback(display, draw_callback, NULL);
446
+ obs_display_destroy(display);
447
+ display = nullptr;
448
+ blog(LOG_INFO, "OBS display destroyed");
449
+ }
450
+
451
+ // Destroy the child window to fully clean up the preview
452
+ if (previewHwnd) {
453
+ DestroyWindow(previewHwnd);
454
+ previewHwnd = nullptr;
455
+ blog(LOG_INFO, "Preview child window destroyed");
456
+ }
457
+ }
458
+
459
+ ObsInterface::ObsInterface(
460
+ const std::string& pluginPath,
461
+ const std::string& logPath,
462
+ const std::string& dataPath,
463
+ const std::string& recordingPath,
464
+ Napi::ThreadSafeFunction cb
465
+ ) {
466
+ // Setup logs first so we have logs for the initialization.
467
+ base_set_log_handler(log_handler, (void*)logPath.c_str());
468
+ blog(LOG_DEBUG, "Creating ObsInterface");
469
+
470
+ // Initialize OBS and load required modules.
471
+ init_obs(pluginPath, dataPath);
472
+
473
+ // Create the resources we rely on.
474
+ output = create_output(recordingPath);
475
+ scene = create_scene();
476
+ video_source = create_video_source();
477
+ obs_scene_add(scene, video_source);
478
+
479
+ // Add the signal handler callback.
480
+ jscb = cb;
481
+ create_signal_handlers(output);
482
+ }
483
+
484
+ ObsInterface::~ObsInterface() {
485
+ blog(LOG_DEBUG, "Destroying ObsInterface");
486
+
487
+ if (jscb) {
488
+ blog(LOG_DEBUG, "Releasing JavaScript callback");
489
+ jscb.Release();
490
+ }
491
+
492
+ if (video_source) {
493
+ blog(LOG_DEBUG, "Releasing video source");
494
+ obs_source_release(video_source);
495
+ }
496
+
497
+ if (scene) {
498
+ blog(LOG_DEBUG, "Releasing scene");
499
+ obs_scene_release(scene);
500
+ }
501
+
502
+ if (output) {
503
+ if (obs_output_active(output)) {
504
+ blog(LOG_DEBUG, "Force stopping output");
505
+ obs_output_force_stop(output);
506
+ }
507
+
508
+ blog(LOG_DEBUG, "Releasing output");
509
+ obs_output_release(output);
510
+ }
511
+
512
+ if (video_encoder) {
513
+ blog(LOG_DEBUG, "Releasing video encoder");
514
+ obs_encoder_release(video_encoder);
515
+ }
516
+
517
+ if (audio_encoder) {
518
+ blog(LOG_DEBUG, "Releasing audio encoder");
519
+ obs_encoder_release(audio_encoder);
520
+ }
521
+
522
+ blog(LOG_DEBUG, "Now shutting down OBS");
523
+ obs_shutdown();
524
+ }
525
+
526
+ void ObsInterface::startBuffering() {
527
+ blog(LOG_INFO, "ObsInterface::startBuffering called");
528
+
529
+ if (!output) {
530
+ throw std::runtime_error("Output is not initialized!");
531
+ }
532
+
533
+ bool is_active = obs_output_active(output);
534
+
535
+ if (is_active) {
536
+ blog(LOG_WARNING, "Output is already active");
537
+ return;
538
+ }
539
+
540
+ bool success = obs_output_start(output);
541
+
542
+ if (!success) {
543
+ throw std::runtime_error("Failed to start buffering!");
544
+ }
545
+
546
+ blog(LOG_INFO, "ObsInterface::startBuffering exited");
547
+ }
548
+
549
+ void ObsInterface::startRecording(int offset) {
550
+ blog(LOG_INFO, "ObsInterface::startRecording enter");
551
+ bool is_active = obs_output_active(output);
552
+
553
+ if (!is_active) {
554
+ blog(LOG_ERROR, "Buffer is not active");
555
+ throw std::runtime_error("Buffer is not active");
556
+ }
557
+
558
+ blog(LOG_INFO, "calling save proc handler");
559
+ calldata cd;
560
+ calldata_init(&cd);
561
+ calldata_set_int(&cd, "offset_seconds", offset);
562
+ proc_handler_t *ph = obs_output_get_proc_handler(output);
563
+ bool success = proc_handler_call(ph, "convert", &cd);
564
+ calldata_free(&cd);
565
+
566
+ if (!success) {
567
+ throw std::runtime_error("Failed to call convert procedure handler");
568
+ }
569
+
570
+ blog(LOG_INFO, "ObsInterface::startRecording exit");
571
+ }
572
+
573
+ void ObsInterface::stopRecording() {
574
+ blog(LOG_INFO, "ObsInterface::stopRecording enter");
575
+ bool is_active = obs_output_active(output);
576
+
577
+ if (!is_active) {
578
+ blog(LOG_WARNING, "Buffer is not active");
579
+ return;
580
+ }
581
+
582
+ obs_output_stop(output);
583
+ blog(LOG_INFO, "ObsInterface::stopRecording exited");
584
+ }
585
+
586
+ std::string ObsInterface::getLastRecording() {
587
+ blog(LOG_INFO, "calling get last replay proc handler");
588
+ calldata cd;
589
+ calldata_init(&cd);
590
+ proc_handler_t *ph = obs_output_get_proc_handler(output);
591
+ bool success = proc_handler_call(ph, "get_last_replay", &cd);
592
+
593
+ if (!success) {
594
+ blog(LOG_ERROR, "Failed to call get_last_replay procedure handler");
595
+ calldata_free(&cd);
596
+ return "";
597
+ }
598
+
599
+ const char* p = calldata_string(&cd, "path");
600
+ std::string path = p ? p : "" ;
601
+ calldata_free(&cd);
602
+
603
+ blog(LOG_INFO, "return path: %s", path.c_str());
604
+ return path;
605
+ }
@@ -0,0 +1,74 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+
5
+ struct SignalData {
6
+ std::string id;
7
+ long long code;
8
+ };
9
+
10
+ class ObsInterface {
11
+ public:
12
+ ObsInterface(
13
+ const std::string& pluginPath, // where to look for plugins
14
+ const std::string& logPath, // where to write logs to
15
+ const std::string& dataPath, // where to look for effects
16
+ const std::string& recordingPath, // where to save recordings
17
+ Napi::ThreadSafeFunction cb // JavaScript callback
18
+ );
19
+
20
+ ~ObsInterface();
21
+
22
+ void startBuffering();
23
+ void startRecording(int offset);
24
+ void stopRecording();
25
+ std::string getLastRecording();
26
+
27
+ void showPreview(HWND hwnd);
28
+ void hidePreview();
29
+ void resizePreview(int width, int height);
30
+ void movePreview(int x, int y);
31
+
32
+ std::vector<std::string> get_available_video_encoders();
33
+
34
+ // TODO
35
+ // Show preview
36
+ // Hide preview
37
+ // Configure video
38
+ // Configure audio
39
+ // List audio source
40
+ // Reconfigure audio sources
41
+ // Reconfigure video sources
42
+
43
+ private:
44
+ obs_output_t *output = nullptr;
45
+ obs_scene_t *scene = nullptr;
46
+ obs_source_t *video_source = nullptr;
47
+ obs_source_t *audio_source = nullptr;
48
+ obs_encoder_t *video_encoder = nullptr;
49
+ obs_encoder_t *audio_encoder = nullptr;
50
+ obs_display_t *display = nullptr;
51
+ HWND previewHwnd = nullptr; // window handle for scene preview
52
+ Napi::ThreadSafeFunction jscb; // javascript callback
53
+
54
+ void init_obs(const std::string& pluginPath, const std::string& dataPath);
55
+ void reset_video();
56
+ void reset_audio();
57
+ void load_module(const char* module);
58
+ void create_signal_handlers(obs_output_t *output);
59
+
60
+ static void output_signal_handler_starting(void *data, calldata_t *cd);
61
+ static void output_signal_handler_start(void *data, calldata_t *cd);
62
+ static void output_signal_handler_stop(void *data, calldata_t *cd);
63
+ static void output_signal_handler_stopping(void *data, calldata_t *cd);
64
+ static void output_signal_handler_saved(void *data, calldata_t *cd);
65
+
66
+ void list_encoders(obs_encoder_type type = OBS_ENCODER_VIDEO);
67
+ void list_source_types();
68
+ void list_input_types();
69
+ void list_output_types();
70
+
71
+ obs_output_t* create_output(const std::string& recordingPath);
72
+ obs_scene_t* create_scene();
73
+ obs_source_t* create_video_source();
74
+ };