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.
- package/COPYING +339 -0
- package/README.md +46 -0
- package/bin/64bit/obs.lib +0 -0
- package/binding.gyp +23 -0
- package/dist/bin/Qt6Core.dll +0 -0
- package/dist/bin/Qt6Gui.dll +0 -0
- package/dist/bin/Qt6Network.dll +0 -0
- package/dist/bin/Qt6Svg.dll +0 -0
- package/dist/bin/Qt6Widgets.dll +0 -0
- package/dist/bin/Qt6Xml.dll +0 -0
- package/dist/bin/avcodec-61.dll +0 -0
- package/dist/bin/avdevice-61.dll +0 -0
- package/dist/bin/avfilter-10.dll +0 -0
- package/dist/bin/avformat-61.dll +0 -0
- package/dist/bin/avutil-59.dll +0 -0
- package/dist/bin/datachannel.dll +0 -0
- package/dist/bin/libcurl.dll +0 -0
- package/dist/bin/libobs-d3d11.dll +0 -0
- package/dist/bin/libobs-opengl.dll +0 -0
- package/dist/bin/libobs-winrt.dll +0 -0
- package/dist/bin/librist.dll +0 -0
- package/dist/bin/libx264-164.dll +0 -0
- package/dist/bin/lua51.dll +0 -0
- package/dist/bin/obs-amf-test.exe +0 -0
- package/dist/bin/obs-ffmpeg-mux.exe +0 -0
- package/dist/bin/obs-frontend-api.dll +0 -0
- package/dist/bin/obs-scripting.dll +0 -0
- package/dist/bin/obs.dll +0 -0
- package/dist/bin/srt.dll +0 -0
- package/dist/bin/swresample-5.dll +0 -0
- package/dist/bin/swscale-8.dll +0 -0
- package/dist/bin/w32-pthreads.dll +0 -0
- package/dist/bin/zlib.dll +0 -0
- package/dist/effects/area.effect +250 -0
- package/dist/effects/bicubic_scale.effect +236 -0
- package/dist/effects/bilinear_lowres_scale.effect +123 -0
- package/dist/effects/color.effect +172 -0
- package/dist/effects/default.effect +254 -0
- package/dist/effects/default_rect.effect +84 -0
- package/dist/effects/deinterlace_base.effect +325 -0
- package/dist/effects/deinterlace_blend.effect +21 -0
- package/dist/effects/deinterlace_blend_2x.effect +21 -0
- package/dist/effects/deinterlace_discard.effect +21 -0
- package/dist/effects/deinterlace_discard_2x.effect +21 -0
- package/dist/effects/deinterlace_linear.effect +21 -0
- package/dist/effects/deinterlace_linear_2x.effect +21 -0
- package/dist/effects/deinterlace_yadif.effect +21 -0
- package/dist/effects/deinterlace_yadif_2x.effect +21 -0
- package/dist/effects/format_conversion.effect +1823 -0
- package/dist/effects/lanczos_scale.effect +292 -0
- package/dist/effects/opaque.effect +159 -0
- package/dist/effects/premultiplied_alpha.effect +38 -0
- package/dist/effects/repeat.effect +36 -0
- package/dist/effects/solid.effect +80 -0
- package/dist/noobs.node +0 -0
- package/dist/plugins/obs-ffmpeg.dll +0 -0
- package/dist/plugins/obs-x264.dll +0 -0
- package/dist/plugins/win-capture.dll +0 -0
- package/include/audio-monitoring/osx/mac-helpers.h +13 -0
- package/include/audio-monitoring/pulse/pulseaudio-wrapper.h +212 -0
- package/include/audio-monitoring/win32/wasapi-output.h +22 -0
- package/include/callback/calldata.h +195 -0
- package/include/callback/decl.h +61 -0
- package/include/callback/proc.h +52 -0
- package/include/callback/signal.h +73 -0
- package/include/graphics/axisang.h +65 -0
- package/include/graphics/bounds.h +108 -0
- package/include/graphics/device-exports.h +177 -0
- package/include/graphics/effect-parser.h +290 -0
- package/include/graphics/effect.h +190 -0
- package/include/graphics/graphics-internal.h +335 -0
- package/include/graphics/graphics.h +1024 -0
- package/include/graphics/half.h +100 -0
- package/include/graphics/image-file.h +124 -0
- package/include/graphics/input.h +34 -0
- package/include/graphics/libnsgif/libnsgif.h +142 -0
- package/include/graphics/math-defs.h +45 -0
- package/include/graphics/math-extra.h +61 -0
- package/include/graphics/matrix3.h +98 -0
- package/include/graphics/matrix4.h +102 -0
- package/include/graphics/plane.h +85 -0
- package/include/graphics/quat.h +170 -0
- package/include/graphics/shader-parser.h +273 -0
- package/include/graphics/srgb.h +177 -0
- package/include/graphics/vec2.h +148 -0
- package/include/graphics/vec3.h +224 -0
- package/include/graphics/vec4.h +241 -0
- package/include/media-io/audio-io.h +228 -0
- package/include/media-io/audio-math.h +43 -0
- package/include/media-io/audio-resampler.h +44 -0
- package/include/media-io/format-conversion.h +50 -0
- package/include/media-io/frame-rate.h +29 -0
- package/include/media-io/media-io-defs.h +20 -0
- package/include/media-io/media-remux.h +37 -0
- package/include/media-io/video-frame.h +64 -0
- package/include/media-io/video-io.h +338 -0
- package/include/media-io/video-scaler.h +43 -0
- package/include/obs-audio-controls.h +250 -0
- package/include/obs-av1.h +47 -0
- package/include/obs-avc.h +55 -0
- package/include/obs-config.h +52 -0
- package/include/obs-data.h +311 -0
- package/include/obs-defs.h +52 -0
- package/include/obs-encoder.h +361 -0
- package/include/obs-ffmpeg-compat.h +13 -0
- package/include/obs-hevc.h +81 -0
- package/include/obs-hotkey.h +271 -0
- package/include/obs-hotkeys.h +653 -0
- package/include/obs-interaction.h +56 -0
- package/include/obs-internal.h +1459 -0
- package/include/obs-missing-files.h +53 -0
- package/include/obs-module.h +181 -0
- package/include/obs-nal.h +37 -0
- package/include/obs-nix-platform.h +53 -0
- package/include/obs-nix-wayland.h +24 -0
- package/include/obs-nix-x11.h +22 -0
- package/include/obs-nix.h +42 -0
- package/include/obs-output.h +96 -0
- package/include/obs-properties.h +364 -0
- package/include/obs-scene.h +127 -0
- package/include/obs-service.h +115 -0
- package/include/obs-source.h +568 -0
- package/include/obs.h +2608 -0
- package/include/obsconfig.h +13 -0
- package/include/obsversion.h +5 -0
- package/include/util/apple/cfstring-utils.h +16 -0
- package/include/util/array-serializer.h +37 -0
- package/include/util/base.h +97 -0
- package/include/util/bitstream.h +28 -0
- package/include/util/bmem.h +94 -0
- package/include/util/buffered-file-serializer.h +32 -0
- package/include/util/c99defs.h +75 -0
- package/include/util/cf-lexer.h +199 -0
- package/include/util/cf-parser.h +281 -0
- package/include/util/circlebuf.h +319 -0
- package/include/util/config-file.h +103 -0
- package/include/util/crc32.h +29 -0
- package/include/util/curl/curl-helper.h +35 -0
- package/include/util/darray.h +606 -0
- package/include/util/deque.h +319 -0
- package/include/util/dstr.h +320 -0
- package/include/util/file-serializer.h +34 -0
- package/include/util/lexer.h +273 -0
- package/include/util/pipe.h +52 -0
- package/include/util/platform.h +223 -0
- package/include/util/profiler.h +97 -0
- package/include/util/serializer.h +158 -0
- package/include/util/simde/check.h +285 -0
- package/include/util/simde/debug-trap.h +117 -0
- package/include/util/simde/hedley.h +2123 -0
- package/include/util/simde/simde-align.h +481 -0
- package/include/util/simde/simde-arch.h +537 -0
- package/include/util/simde/simde-common.h +918 -0
- package/include/util/simde/simde-constify.h +925 -0
- package/include/util/simde/simde-detect-clang.h +114 -0
- package/include/util/simde/simde-diagnostic.h +447 -0
- package/include/util/simde/simde-features.h +550 -0
- package/include/util/simde/simde-math.h +1858 -0
- package/include/util/simde/x86/mmx.h +2456 -0
- package/include/util/simde/x86/sse.h +4479 -0
- package/include/util/simde/x86/sse2.h +7549 -0
- package/include/util/source-profiler.h +66 -0
- package/include/util/sse-intrin.h +32 -0
- package/include/util/task.h +22 -0
- package/include/util/text-lookup.h +45 -0
- package/include/util/threading-posix.h +77 -0
- package/include/util/threading-windows.h +142 -0
- package/include/util/threading.h +103 -0
- package/include/util/utf8.h +35 -0
- package/include/util/uthash.h +34 -0
- package/include/util/util_uint128.h +108 -0
- package/include/util/util_uint64.h +34 -0
- package/include/util/windows/device-enum.h +14 -0
- package/include/util/windows/obfuscate.h +16 -0
- package/include/util/windows/win-registry.h +37 -0
- package/include/util/windows/win-version.h +57 -0
- package/include/util/windows/window-helpers.h +47 -0
- package/index.d.ts +38 -0
- package/index.js +8 -0
- package/package.json +31 -0
- package/src/main.cpp +321 -0
- package/src/obs_interface.cpp +605 -0
- package/src/obs_interface.h +74 -0
- package/src/utils.cpp +80 -0
- 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
|
+
};
|