noobs 0.0.30 → 0.0.32
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/dist/noobs.node +0 -0
- package/index.d.ts +28 -13
- package/package.json +1 -1
- package/src/main.cpp +72 -9
- package/src/obs_interface.cpp +121 -30
- package/src/obs_interface.h +8 -3
package/dist/noobs.node
CHANGED
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
type Signal = {
|
|
2
|
-
id: string;
|
|
3
|
-
code: number;
|
|
1
|
+
export type Signal = {
|
|
2
|
+
id: string; // Signal identifier, e.g. "stop"
|
|
3
|
+
code: number; // 0 for success, other values for errors
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type SceneItemPosition = {
|
|
7
|
+
x: number; // X position in pixels
|
|
8
|
+
y: number; // Y position in pixels
|
|
9
|
+
scaleX: number; // X scaling factor
|
|
10
|
+
scaleY: number; // Y scaling factor
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type SourceDimensions = {
|
|
14
|
+
height: number; // Height in pixels, before scaling
|
|
15
|
+
width: number; // Width in pixels, before scaling
|
|
4
16
|
}
|
|
5
17
|
|
|
6
18
|
interface Noobs {
|
|
7
|
-
|
|
19
|
+
Init(
|
|
8
20
|
pluginPath: string,
|
|
9
21
|
logPath: string,
|
|
10
22
|
dataPath: string,
|
|
@@ -12,16 +24,19 @@ interface Noobs {
|
|
|
12
24
|
cb: (signal: Signal) => void
|
|
13
25
|
): void;
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
Shutdown(): void;
|
|
28
|
+
StartBuffer(): void;
|
|
29
|
+
StartRecording(offset: number): void;
|
|
30
|
+
StopRecording(): void;
|
|
31
|
+
GetLastRecording(): string;
|
|
32
|
+
|
|
33
|
+
GetSourcePos(src: string): SceneItemPosition & SourceDimensions;
|
|
34
|
+
SetSourcePos(src: string, pos: SceneItemPosition): void;
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
InitPreview(hwnd: Buffer): void;
|
|
37
|
+
ShowPreview(x: number, y: number, width: number, height: number): void;
|
|
38
|
+
HidePreview(): void;
|
|
24
39
|
}
|
|
25
40
|
|
|
26
41
|
declare const noobs: Noobs;
|
|
27
|
-
export
|
|
42
|
+
export default noobs;
|
package/package.json
CHANGED
package/src/main.cpp
CHANGED
|
@@ -157,18 +157,81 @@ Napi::Value ObsHidePreview(const Napi::CallbackInfo& info) {
|
|
|
157
157
|
return info.Env().Undefined();
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
Napi::Value ObsGetSourcePos(const Napi::CallbackInfo& info) {
|
|
161
|
+
if (!obs) {
|
|
162
|
+
blog(LOG_ERROR, "ObsUpdateSource called but obs is not initialized");
|
|
163
|
+
throw std::runtime_error("Obs not initialized");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
bool valid = info.Length() == 1 && info[0].IsString();
|
|
167
|
+
|
|
168
|
+
if (!valid) {
|
|
169
|
+
Napi::TypeError::New(info.Env(), "Invalid arguments passed to ObsGetSourcePos").ThrowAsJavaScriptException();
|
|
170
|
+
return info.Env().Undefined();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
std::string name = info[0].As<Napi::String>().Utf8Value();
|
|
174
|
+
|
|
175
|
+
vec2 pos; vec2 size; vec2 scale;
|
|
176
|
+
obs->getSourcePos(name, &pos, &size, &scale);
|
|
177
|
+
|
|
178
|
+
Napi::Object result = Napi::Object::New(info.Env());
|
|
179
|
+
result.Set("x", Napi::Number::New(info.Env(), pos.x));
|
|
180
|
+
result.Set("y", Napi::Number::New(info.Env(), pos.y));
|
|
181
|
+
result.Set("width", Napi::Number::New(info.Env(), size.x));
|
|
182
|
+
result.Set("height", Napi::Number::New(info.Env(), size.y));
|
|
183
|
+
result.Set("scaleX", Napi::Number::New(info.Env(), scale.x));
|
|
184
|
+
result.Set("scaleY", Napi::Number::New(info.Env(), scale.y));
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
Napi::Value ObsSetSourcePos(const Napi::CallbackInfo& info) {
|
|
189
|
+
if (!obs) {
|
|
190
|
+
blog(LOG_ERROR, "ObsUpdateSource called but obs is not initialized");
|
|
191
|
+
throw std::runtime_error("Obs not initialized");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
bool valid = info.Length() == 6 &&
|
|
195
|
+
info[0].IsString() && // Source name
|
|
196
|
+
info[1].IsNumber() && // X position (px)
|
|
197
|
+
info[2].IsNumber() && // Y position (px)
|
|
198
|
+
info[3].IsNumber() && // Scale factor (X)
|
|
199
|
+
info[4].IsNumber(); // Scale factor (Y)
|
|
200
|
+
|
|
201
|
+
if (!valid) {
|
|
202
|
+
Napi::TypeError::New(info.Env(), "Invalid arguments passed to ObsSetSourcePos").ThrowAsJavaScriptException();
|
|
203
|
+
return info.Env().Undefined();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
std::string name = info[0].As<Napi::String>().Utf8Value();
|
|
207
|
+
|
|
208
|
+
float x = info[1].As<Napi::Number>().FloatValue();
|
|
209
|
+
float y = info[2].As<Napi::Number>().FloatValue();
|
|
210
|
+
vec2 pos = { x, y };
|
|
211
|
+
|
|
212
|
+
float scaleX = info[3].As<Napi::Number>().FloatValue();
|
|
213
|
+
float scaleY = info[4].As<Napi::Number>().FloatValue();
|
|
214
|
+
vec2 scale = { scaleX, scaleY };
|
|
215
|
+
|
|
216
|
+
obs->setSourcePos(name, &pos, &scale);
|
|
217
|
+
return info.Env().Undefined();
|
|
218
|
+
}
|
|
219
|
+
|
|
160
220
|
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
161
|
-
exports.Set("
|
|
162
|
-
exports.Set("
|
|
221
|
+
exports.Set("Init", Napi::Function::New(env, ObsInit));
|
|
222
|
+
exports.Set("Shutdown", Napi::Function::New(env, ObsShutdown));
|
|
223
|
+
|
|
224
|
+
exports.Set("StartBuffer", Napi::Function::New(env, ObsStartBuffer));
|
|
225
|
+
exports.Set("StartRecording", Napi::Function::New(env, ObsStartRecording));
|
|
226
|
+
exports.Set("StopRecording", Napi::Function::New(env, ObsStopRecording));
|
|
227
|
+
exports.Set("GetLastRecording", Napi::Function::New(env, ObsGetLastRecording));
|
|
163
228
|
|
|
164
|
-
exports.Set("
|
|
165
|
-
exports.Set("
|
|
166
|
-
exports.Set("ObsStopRecording", Napi::Function::New(env, ObsStopRecording));
|
|
167
|
-
exports.Set("ObsGetLastRecording", Napi::Function::New(env, ObsGetLastRecording));
|
|
229
|
+
exports.Set("GetSourcePos", Napi::Function::New(env, ObsGetSourcePos));
|
|
230
|
+
exports.Set("SetSourcePos", Napi::Function::New(env, ObsSetSourcePos));
|
|
168
231
|
|
|
169
|
-
exports.Set("
|
|
170
|
-
exports.Set("
|
|
171
|
-
exports.Set("
|
|
232
|
+
exports.Set("InitPreview", Napi::Function::New(env, ObsInitPreview));
|
|
233
|
+
exports.Set("ShowPreview", Napi::Function::New(env, ObsShowPreview));
|
|
234
|
+
exports.Set("HidePreview", Napi::Function::New(env, ObsHidePreview));
|
|
172
235
|
return exports;
|
|
173
236
|
}
|
|
174
237
|
|
package/src/obs_interface.cpp
CHANGED
|
@@ -178,15 +178,26 @@ void ObsInterface::init_obs(const std::string& pluginPath, const std::string& da
|
|
|
178
178
|
blog(LOG_INFO, "Exit init_obs");
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
void ObsInterface::configure_output(const std::string& recordingPath) {
|
|
182
182
|
blog(LOG_INFO, "Create output");
|
|
183
|
-
|
|
183
|
+
|
|
184
|
+
if (output && obs_output_active(output)) {
|
|
185
|
+
blog(LOG_ERROR, "Tried to recreate active output");
|
|
186
|
+
throw std::runtime_error("Failed to create output!");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (output) {
|
|
190
|
+
blog(LOG_DEBUG, "Releasing output");
|
|
191
|
+
obs_output_release(output);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
output = obs_output_create("replay_buffer", "recording_output", NULL, NULL);
|
|
184
195
|
|
|
185
196
|
if (!output) {
|
|
186
197
|
blog(LOG_ERROR, "Failed to create output!");
|
|
187
198
|
throw std::runtime_error("Failed to create output!");
|
|
188
199
|
}
|
|
189
|
-
|
|
200
|
+
|
|
190
201
|
blog(LOG_INFO, "Set output settings");
|
|
191
202
|
obs_data_t *settings = obs_data_create();
|
|
192
203
|
obs_data_set_int(settings, "max_time_sec", 60);
|
|
@@ -197,7 +208,24 @@ obs_output_t* ObsInterface::create_output(const std::string& recordingPath) {
|
|
|
197
208
|
obs_output_update(output, settings);
|
|
198
209
|
obs_data_release(settings);
|
|
199
210
|
|
|
200
|
-
|
|
211
|
+
// Add the signal handler callback.
|
|
212
|
+
create_signal_handlers(output);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
void ObsInterface::configure_video_encoder() {
|
|
216
|
+
blog(LOG_INFO, "Create video encoder");
|
|
217
|
+
|
|
218
|
+
if (!output) {
|
|
219
|
+
blog(LOG_ERROR, "No output on configure_video_encoder");
|
|
220
|
+
throw std::runtime_error("Failed to create video encoder!");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (video_encoder) {
|
|
224
|
+
blog(LOG_DEBUG, "Releasing video encoder");
|
|
225
|
+
obs_encoder_release(video_encoder);
|
|
226
|
+
video_encoder = nullptr;
|
|
227
|
+
}
|
|
228
|
+
|
|
201
229
|
video_encoder = obs_video_encoder_create("obs_x264", "simple_h264_stream", NULL, NULL);
|
|
202
230
|
|
|
203
231
|
if (!video_encoder) {
|
|
@@ -208,15 +236,28 @@ obs_output_t* ObsInterface::create_output(const std::string& recordingPath) {
|
|
|
208
236
|
blog(LOG_INFO, "Set video encoder settings");
|
|
209
237
|
obs_data_t* venc_settings = obs_data_create();
|
|
210
238
|
obs_data_set_string(venc_settings, "preset", "speed"); // Faster preset
|
|
211
|
-
//obs_data_set_int(amf_settings, "bitrate", 2500);
|
|
212
239
|
obs_data_set_string(venc_settings, "rate_control", "CRF");
|
|
213
240
|
obs_data_set_int(venc_settings, "crf", 30);
|
|
214
241
|
obs_data_set_string(venc_settings, "profile", "main");
|
|
215
242
|
obs_data_set_int(venc_settings, "keyint_sec", 1); // Set keyframe interval to 1 second
|
|
216
243
|
obs_encoder_update(video_encoder, venc_settings);
|
|
217
244
|
obs_data_release(venc_settings);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
void ObsInterface::configure_audio_encoder() {
|
|
248
|
+
blog(LOG_INFO, "Create audio encoder");
|
|
249
|
+
|
|
250
|
+
if (!output) {
|
|
251
|
+
blog(LOG_ERROR, "No output on configure_audio_encoder");
|
|
252
|
+
throw std::runtime_error("Failed to create audio encoder!");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (audio_encoder) {
|
|
256
|
+
blog(LOG_DEBUG, "Releasing audio encoder");
|
|
257
|
+
obs_encoder_release(audio_encoder);
|
|
258
|
+
audio_encoder = nullptr;
|
|
259
|
+
}
|
|
218
260
|
|
|
219
|
-
blog(LOG_INFO, "Create aenc");
|
|
220
261
|
audio_encoder = obs_audio_encoder_create("ffmpeg_aac", "simple_aac", NULL, 0, NULL);
|
|
221
262
|
|
|
222
263
|
if (!audio_encoder) {
|
|
@@ -235,40 +276,50 @@ obs_output_t* ObsInterface::create_output(const std::string& recordingPath) {
|
|
|
235
276
|
|
|
236
277
|
obs_encoder_set_video(video_encoder, obs_get_video());
|
|
237
278
|
obs_encoder_set_audio(audio_encoder, obs_get_audio());
|
|
238
|
-
|
|
239
|
-
return output;
|
|
240
279
|
}
|
|
241
280
|
|
|
242
|
-
|
|
243
|
-
blog(LOG_INFO, "
|
|
244
|
-
obs_scene_t *scene = obs_scene_create("WCR Scene");
|
|
281
|
+
void ObsInterface::configure_scene() {
|
|
282
|
+
blog(LOG_INFO, "Configure scene");
|
|
245
283
|
|
|
246
|
-
if (
|
|
284
|
+
if (scene) {
|
|
285
|
+
blog(LOG_DEBUG, "Releasing scene");
|
|
286
|
+
obs_scene_release(scene);
|
|
287
|
+
scene = nullptr;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
scene = obs_scene_create("WCR Scene");
|
|
291
|
+
|
|
292
|
+
if (!scene) {
|
|
293
|
+
blog(LOG_ERROR, "Failed to create scene!");
|
|
247
294
|
throw std::runtime_error("Failed to create scene!");
|
|
295
|
+
}
|
|
248
296
|
|
|
249
297
|
obs_source_t *scene_source = obs_scene_get_source(scene);
|
|
250
298
|
|
|
251
|
-
if (!scene_source)
|
|
252
|
-
|
|
299
|
+
if (!scene_source) {
|
|
300
|
+
blog(LOG_ERROR, "Failed to get scene source!");
|
|
301
|
+
throw std::runtime_error("Failed to get scene source!");
|
|
302
|
+
}
|
|
253
303
|
|
|
254
|
-
obs_set_output_source(0, scene_source);
|
|
255
|
-
|
|
304
|
+
obs_set_output_source(0, scene_source); // 0 = video track
|
|
305
|
+
obs_scene_add(scene, video_source);
|
|
256
306
|
}
|
|
257
307
|
|
|
258
|
-
|
|
259
|
-
blog(LOG_INFO, "Create
|
|
308
|
+
void ObsInterface::configure_video_source() {
|
|
309
|
+
blog(LOG_INFO, "Create monitor capture source");
|
|
310
|
+
|
|
260
311
|
// Create settings for monitor capture
|
|
261
312
|
obs_data_t *monitor_settings = obs_data_create();
|
|
262
313
|
obs_data_set_int(monitor_settings, "monitor", 0); // Monitor 0
|
|
263
314
|
obs_data_set_bool(monitor_settings, "capture_cursor", true);
|
|
264
315
|
|
|
265
|
-
|
|
316
|
+
video_source = obs_source_create("monitor_capture", "video_source", monitor_settings, NULL);
|
|
266
317
|
obs_data_release(monitor_settings);
|
|
267
318
|
|
|
268
|
-
if (!
|
|
319
|
+
if (!video_source) {
|
|
320
|
+
blog(LOG_ERROR, "Failed to create video source!");
|
|
269
321
|
throw std::runtime_error("Failed to create video source!");
|
|
270
|
-
|
|
271
|
-
return source;
|
|
322
|
+
}
|
|
272
323
|
}
|
|
273
324
|
|
|
274
325
|
void call_jscb(Napi::Env env, Napi::Function cb, SignalData* sd) {
|
|
@@ -428,15 +479,15 @@ ObsInterface::ObsInterface(
|
|
|
428
479
|
// Initialize OBS and load required modules.
|
|
429
480
|
init_obs(pluginPath, dataPath);
|
|
430
481
|
|
|
431
|
-
//
|
|
432
|
-
output = create_output(recordingPath);
|
|
433
|
-
scene = create_scene();
|
|
434
|
-
video_source = create_video_source();
|
|
435
|
-
obs_scene_add(scene, video_source);
|
|
436
|
-
|
|
437
|
-
// Add the signal handler callback.
|
|
482
|
+
// Setup callback function.
|
|
438
483
|
jscb = cb;
|
|
439
|
-
|
|
484
|
+
|
|
485
|
+
// Create the resources we rely on.
|
|
486
|
+
configure_output(recordingPath);
|
|
487
|
+
configure_video_encoder();
|
|
488
|
+
configure_audio_encoder();
|
|
489
|
+
configure_video_source();
|
|
490
|
+
configure_scene();
|
|
440
491
|
}
|
|
441
492
|
|
|
442
493
|
ObsInterface::~ObsInterface() {
|
|
@@ -560,4 +611,44 @@ std::string ObsInterface::getLastRecording() {
|
|
|
560
611
|
|
|
561
612
|
blog(LOG_INFO, "return path: %s", path.c_str());
|
|
562
613
|
return path;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
void ObsInterface::getSourcePos(std::string name, vec2* pos, vec2* size, vec2* scale)
|
|
617
|
+
{
|
|
618
|
+
blog(LOG_INFO, "ObsInterface::getSourcePos called");
|
|
619
|
+
|
|
620
|
+
obs_source_t *src = obs_get_source_by_name(name.c_str());
|
|
621
|
+
obs_sceneitem_t *item = obs_scene_find_source(scene, name.c_str());
|
|
622
|
+
|
|
623
|
+
if (!src) {
|
|
624
|
+
blog(LOG_ERROR, "Did not find source for video ");
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
if (!item) {
|
|
629
|
+
blog(LOG_ERROR, "Did not find scene item for video source");
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
obs_sceneitem_get_pos(item, pos);
|
|
634
|
+
obs_sceneitem_get_scale(item, scale);
|
|
635
|
+
|
|
636
|
+
// Pre-scaled sizes.
|
|
637
|
+
size->x = obs_source_get_width(src);
|
|
638
|
+
size->y = obs_source_get_height(src);
|
|
639
|
+
|
|
640
|
+
blog(LOG_INFO, "ObsInterface::getSourcePos exited");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
void ObsInterface::setSourcePos(std::string name, vec2* pos, vec2* scale) {
|
|
644
|
+
blog(LOG_INFO, "ObsInterface::moveSource called");
|
|
645
|
+
obs_sceneitem_t *item = obs_scene_find_source(scene, name.c_str());
|
|
646
|
+
|
|
647
|
+
if (!item) {
|
|
648
|
+
blog(LOG_ERROR, "Did not find scene item for video source");
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
obs_sceneitem_set_pos(item, pos);
|
|
653
|
+
obs_sceneitem_set_scale(item, scale);
|
|
563
654
|
}
|
package/src/obs_interface.h
CHANGED
|
@@ -24,6 +24,9 @@ class ObsInterface {
|
|
|
24
24
|
void stopRecording();
|
|
25
25
|
std::string getLastRecording();
|
|
26
26
|
|
|
27
|
+
void getSourcePos(std::string name, vec2* pos, vec2* size, vec2* scale); // Size is returned to allow clients to calculate scale.
|
|
28
|
+
void setSourcePos(std::string name, vec2* pos, vec2* scale); // Size does not get set here because it's set by the source itself.
|
|
29
|
+
|
|
27
30
|
void initPreview(HWND parent); // Must call this before showPreview to setup resources.
|
|
28
31
|
void showPreview(int x, int y, int width, int height); // Also used for moving and resizing.
|
|
29
32
|
void hidePreview();
|
|
@@ -65,7 +68,9 @@ class ObsInterface {
|
|
|
65
68
|
void list_input_types();
|
|
66
69
|
void list_output_types();
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
void configure_output(const std::string& recordingPath);
|
|
72
|
+
void configure_video_encoder();
|
|
73
|
+
void configure_audio_encoder();
|
|
74
|
+
void configure_scene();
|
|
75
|
+
void configure_video_source();
|
|
71
76
|
};
|