noobs 0.0.32 → 0.0.60
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/dist/plugins/image-source.dll +0 -0
- package/dist/plugins/win-wasapi.dll +0 -0
- package/index.d.ts +150 -3
- package/package.json +1 -1
- package/src/main.cpp +212 -25
- package/src/obs_interface.cpp +322 -60
- package/src/obs_interface.h +24 -13
- package/src/utils.cpp +317 -1
- package/src/utils.h +10 -1
package/src/obs_interface.cpp
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
#include "obs_interface.h"
|
|
7
7
|
#include <vector>
|
|
8
8
|
#include <thread>
|
|
9
|
+
#include <iostream>
|
|
10
|
+
#include <map>
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <graphics/matrix4.h>
|
|
13
|
+
#include <graphics/vec4.h>
|
|
14
|
+
#include <util/platform.h>
|
|
9
15
|
|
|
10
16
|
std::vector<std::string> ObsInterface::get_available_video_encoders()
|
|
11
17
|
{
|
|
@@ -158,7 +164,14 @@ void ObsInterface::init_obs(const std::string& pluginPath, const std::string& da
|
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
obs_add_data_path(dp.c_str()); // This is deprecated in libobs but it works for now.
|
|
161
|
-
|
|
167
|
+
|
|
168
|
+
std::vector<std::string> modules = {
|
|
169
|
+
"obs-x264.dll",
|
|
170
|
+
"obs-ffmpeg.dll",
|
|
171
|
+
"win-capture.dll", // Required for basically all forms of capture on Windows.
|
|
172
|
+
"image-source.dll", // Required for image sources.
|
|
173
|
+
"win-wasapi.dll" // Required for WASAPI audio input.
|
|
174
|
+
};
|
|
162
175
|
|
|
163
176
|
for (const auto& module : modules) {
|
|
164
177
|
std::string path = pluginPath + "/" + module;
|
|
@@ -178,33 +191,46 @@ void ObsInterface::init_obs(const std::string& pluginPath, const std::string& da
|
|
|
178
191
|
blog(LOG_INFO, "Exit init_obs");
|
|
179
192
|
}
|
|
180
193
|
|
|
181
|
-
void ObsInterface::
|
|
194
|
+
void ObsInterface::create_output(const std::string& recordingPath, bool buffering) {
|
|
182
195
|
blog(LOG_INFO, "Create output");
|
|
183
196
|
|
|
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
197
|
if (output) {
|
|
190
198
|
blog(LOG_DEBUG, "Releasing output");
|
|
191
199
|
obs_output_release(output);
|
|
192
200
|
}
|
|
193
201
|
|
|
194
|
-
|
|
202
|
+
if (buffering) {
|
|
203
|
+
blog(LOG_INFO, "Creating replay buffer output");
|
|
204
|
+
output = obs_output_create("replay_buffer", "recording_output", NULL, NULL);
|
|
205
|
+
} else {
|
|
206
|
+
blog(LOG_INFO, "Creating file output");
|
|
207
|
+
output = obs_output_create("ffmpeg_muxer", "recording_output", NULL, NULL);
|
|
208
|
+
}
|
|
195
209
|
|
|
196
210
|
if (!output) {
|
|
197
211
|
blog(LOG_ERROR, "Failed to create output!");
|
|
198
212
|
throw std::runtime_error("Failed to create output!");
|
|
199
213
|
}
|
|
200
|
-
|
|
201
|
-
blog(LOG_INFO, "Set output settings");
|
|
214
|
+
|
|
202
215
|
obs_data_t *settings = obs_data_create();
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
216
|
+
|
|
217
|
+
if (buffering) {
|
|
218
|
+
blog(LOG_INFO, "Set replay_buffer settings");
|
|
219
|
+
obs_data_set_int(settings, "max_time_sec", 60);
|
|
220
|
+
obs_data_set_int(settings, "max_size_mb", 1024);
|
|
221
|
+
obs_data_set_string(settings, "directory", recordingPath.c_str());
|
|
222
|
+
obs_data_set_string(settings, "format", "%CCYY-%MM-%DD %hh-%mm-%ss");
|
|
223
|
+
obs_data_set_string(settings, "extension", "mp4");
|
|
224
|
+
} else {
|
|
225
|
+
blog(LOG_INFO, "Set ffmpeg_muxer settings");
|
|
226
|
+
obs_data_set_string(settings, "extension", "mp4");
|
|
227
|
+
// Apparently need to specify the exact path for ffmpeg_muxer.
|
|
228
|
+
// TODO add something to auto generate this ?
|
|
229
|
+
obs_data_set_string(settings, "path", (recordingPath + "/noobs.mp4").c_str());
|
|
230
|
+
recording_path = recordingPath + "/noobs.mp4";
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Apply and release the settings.
|
|
208
234
|
obs_output_update(output, settings);
|
|
209
235
|
obs_data_release(settings);
|
|
210
236
|
|
|
@@ -212,6 +238,32 @@ void ObsInterface::configure_output(const std::string& recordingPath) {
|
|
|
212
238
|
create_signal_handlers(output);
|
|
213
239
|
}
|
|
214
240
|
|
|
241
|
+
void ObsInterface::updateRecordingDir(const std::string& recordingPath) {
|
|
242
|
+
blog(LOG_INFO, "Updating recording directory");
|
|
243
|
+
|
|
244
|
+
if (!output) {
|
|
245
|
+
blog(LOG_ERROR, "No output to update recording directory");
|
|
246
|
+
throw std::runtime_error("Output not initialized");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// check its not active
|
|
250
|
+
if (obs_output_active(output)) {
|
|
251
|
+
blog(LOG_ERROR, "Output is active, cannot update recording path");
|
|
252
|
+
throw std::runtime_error("Output is active, cannot update recording path");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
obs_data_t *settings = obs_output_get_settings(output);
|
|
256
|
+
|
|
257
|
+
if (!settings) {
|
|
258
|
+
blog(LOG_ERROR, "Failed to get output settings");
|
|
259
|
+
throw std::runtime_error("Failed to get output settings");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
obs_data_set_string(settings, "directory", recordingPath.c_str());
|
|
263
|
+
obs_output_update(output, settings);
|
|
264
|
+
obs_data_release(settings);
|
|
265
|
+
}
|
|
266
|
+
|
|
215
267
|
void ObsInterface::configure_video_encoder() {
|
|
216
268
|
blog(LOG_INFO, "Create video encoder");
|
|
217
269
|
|
|
@@ -278,15 +330,8 @@ void ObsInterface::configure_audio_encoder() {
|
|
|
278
330
|
obs_encoder_set_audio(audio_encoder, obs_get_audio());
|
|
279
331
|
}
|
|
280
332
|
|
|
281
|
-
void ObsInterface::
|
|
282
|
-
blog(LOG_INFO, "
|
|
283
|
-
|
|
284
|
-
if (scene) {
|
|
285
|
-
blog(LOG_DEBUG, "Releasing scene");
|
|
286
|
-
obs_scene_release(scene);
|
|
287
|
-
scene = nullptr;
|
|
288
|
-
}
|
|
289
|
-
|
|
333
|
+
void ObsInterface::create_scene() {
|
|
334
|
+
blog(LOG_INFO, "Create scene");
|
|
290
335
|
scene = obs_scene_create("WCR Scene");
|
|
291
336
|
|
|
292
337
|
if (!scene) {
|
|
@@ -302,24 +347,85 @@ void ObsInterface::configure_scene() {
|
|
|
302
347
|
}
|
|
303
348
|
|
|
304
349
|
obs_set_output_source(0, scene_source); // 0 = video track
|
|
305
|
-
obs_scene_add(scene, video_source);
|
|
306
350
|
}
|
|
307
351
|
|
|
308
|
-
void ObsInterface::
|
|
309
|
-
blog(LOG_INFO, "Create
|
|
352
|
+
void ObsInterface::createSource(std::string name, std::string type) {
|
|
353
|
+
blog(LOG_INFO, "Create source: %s of type %s", name.c_str(), type.c_str());
|
|
354
|
+
|
|
355
|
+
obs_source_t *source = obs_source_create(
|
|
356
|
+
type.c_str(),
|
|
357
|
+
name.c_str(),
|
|
358
|
+
NULL, // No settings.
|
|
359
|
+
NULL // No hotkey data.
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
if (!source) {
|
|
363
|
+
blog(LOG_ERROR, "Failed to create source: %s", name.c_str());
|
|
364
|
+
throw std::runtime_error("Failed to create source!");
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
void ObsInterface::deleteSource(std::string name) {
|
|
369
|
+
blog(LOG_INFO, "Delete source: %s", name.c_str());
|
|
370
|
+
obs_source_t *source = obs_get_source_by_name(name.c_str());
|
|
371
|
+
|
|
372
|
+
if (!source) {
|
|
373
|
+
blog(LOG_WARNING, "Source not found: %s", name.c_str());
|
|
374
|
+
return; // Source not found, nothing to delete.
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
obs_source_release(source);
|
|
378
|
+
blog(LOG_INFO, "Source deleted: %s", name.c_str());
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
obs_data_t* ObsInterface::getSourceSettings(std::string name) {
|
|
382
|
+
blog(LOG_INFO, "Get source settings for: %s", name.c_str());
|
|
383
|
+
obs_source_t *source = obs_get_source_by_name(name.c_str());
|
|
384
|
+
|
|
385
|
+
if (!source) {
|
|
386
|
+
blog(LOG_ERROR, "Source not found: %s", name.c_str());
|
|
387
|
+
throw std::runtime_error("Source not found!");
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
obs_data_t *settings = obs_source_get_settings(source);
|
|
391
|
+
|
|
392
|
+
if (!settings) {
|
|
393
|
+
blog(LOG_ERROR, "Failed to get settings for source: %s", name.c_str());
|
|
394
|
+
throw std::runtime_error("Failed to get source settings!");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return settings;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
void ObsInterface::setSourceSettings(std::string name, obs_data_t* settings) {
|
|
401
|
+
blog(LOG_INFO, "Set source settings for: %s", name.c_str());
|
|
402
|
+
obs_source_t *source = obs_get_source_by_name(name.c_str());
|
|
403
|
+
|
|
404
|
+
if (!source) {
|
|
405
|
+
blog(LOG_ERROR, "Source not found: %s", name.c_str());
|
|
406
|
+
throw std::runtime_error("Source not found!");
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
obs_source_update(source, settings);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
|
|
413
|
+
blog(LOG_INFO, "Get source properties for: %s", name.c_str());
|
|
414
|
+
obs_source_t *source = obs_get_source_by_name(name.c_str());
|
|
310
415
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
416
|
+
if (!source) {
|
|
417
|
+
blog(LOG_ERROR, "Source not found: %s", name.c_str());
|
|
418
|
+
throw std::runtime_error("Source not found!");
|
|
419
|
+
}
|
|
315
420
|
|
|
316
|
-
|
|
317
|
-
obs_data_release(monitor_settings);
|
|
421
|
+
obs_properties_t *props = obs_source_properties(source);
|
|
318
422
|
|
|
319
|
-
if (!
|
|
320
|
-
blog(LOG_ERROR, "Failed to
|
|
321
|
-
throw std::runtime_error("Failed to
|
|
423
|
+
if (!props) {
|
|
424
|
+
blog(LOG_ERROR, "Failed to get properties for source: %s", name.c_str());
|
|
425
|
+
throw std::runtime_error("Failed to get source properties!");
|
|
322
426
|
}
|
|
427
|
+
|
|
428
|
+
return props;
|
|
323
429
|
}
|
|
324
430
|
|
|
325
431
|
void call_jscb(Napi::Env env, Napi::Function cb, SignalData* sd) {
|
|
@@ -374,8 +480,84 @@ void ObsInterface::create_signal_handlers(obs_output_t *output) {
|
|
|
374
480
|
signal_handler_connect(sh, "saved", output_signal_handler_saved, this);
|
|
375
481
|
}
|
|
376
482
|
|
|
483
|
+
bool draw_box(obs_scene_t *scene, obs_sceneitem_t *item, void *p) {
|
|
484
|
+
// Get the item position and size
|
|
485
|
+
vec2 pos; vec2 scale;
|
|
486
|
+
|
|
487
|
+
obs_sceneitem_get_pos(item, &pos);
|
|
488
|
+
obs_sceneitem_get_scale(item, &scale);
|
|
489
|
+
|
|
490
|
+
obs_source_t *src = obs_sceneitem_get_source(item);
|
|
491
|
+
uint32_t sizex = obs_source_get_width(src);
|
|
492
|
+
uint32_t sizey = obs_source_get_height(src);
|
|
493
|
+
|
|
494
|
+
// Calculate actual size with scaling
|
|
495
|
+
float width = sizex * scale.x;
|
|
496
|
+
float height = sizey * scale.y;
|
|
497
|
+
|
|
498
|
+
// Draw rectangle around the source using the position and size
|
|
499
|
+
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
|
|
500
|
+
gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
|
|
501
|
+
gs_technique_t *tech = gs_effect_get_technique(solid, "Solid");
|
|
502
|
+
|
|
503
|
+
vec4 col = {0.733f, 0.267f, 0.125f, 1.0f}; // #BB4420
|
|
504
|
+
gs_effect_set_vec4(color, &col);
|
|
505
|
+
|
|
506
|
+
gs_technique_begin(tech);
|
|
507
|
+
gs_technique_begin_pass(tech, 0);
|
|
508
|
+
|
|
509
|
+
gs_matrix_push();
|
|
510
|
+
gs_matrix_identity();
|
|
511
|
+
|
|
512
|
+
// Top border
|
|
513
|
+
gs_matrix_push();
|
|
514
|
+
gs_matrix_translate3f(pos.x, pos.y, 0.0f);
|
|
515
|
+
//gs_matrix_scale3f(width, 1.0f, 1.0f);
|
|
516
|
+
gs_draw_sprite(nullptr, 0, width, 1.0f);
|
|
517
|
+
gs_matrix_pop();
|
|
518
|
+
|
|
519
|
+
// Bottom border
|
|
520
|
+
gs_matrix_push();
|
|
521
|
+
gs_matrix_translate3f(pos.x, pos.y + height - 1.0f, 0.0f);
|
|
522
|
+
// gs_matrix_scale3f(width, 1.0f, 1.0f);
|
|
523
|
+
gs_draw_sprite(nullptr, 0, width, 1.0f);
|
|
524
|
+
gs_matrix_pop();
|
|
525
|
+
|
|
526
|
+
// Left border
|
|
527
|
+
gs_matrix_push();
|
|
528
|
+
gs_matrix_translate3f(pos.x, pos.y, 0.0f);
|
|
529
|
+
// gs_matrix_scale3f(1.0f, height, 1.0f);
|
|
530
|
+
gs_draw_sprite(nullptr, 0, 1.0f, height);
|
|
531
|
+
gs_matrix_pop();
|
|
532
|
+
|
|
533
|
+
// Right border
|
|
534
|
+
gs_matrix_push();
|
|
535
|
+
gs_matrix_translate3f(pos.x + width - 1.0f, pos.y, 0.0f);
|
|
536
|
+
// gs_matrix_scale3f(1.0f, height, 1.0f);
|
|
537
|
+
gs_draw_sprite(nullptr, 0, 1.0f, height);
|
|
538
|
+
gs_matrix_pop();
|
|
539
|
+
|
|
540
|
+
gs_matrix_pop();
|
|
541
|
+
|
|
542
|
+
gs_technique_end_pass(tech);
|
|
543
|
+
gs_technique_end(tech);
|
|
544
|
+
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
|
|
377
548
|
void draw_callback(void* data, uint32_t cx, uint32_t cy) {
|
|
549
|
+
// Initially, draw the OBS scene texture
|
|
378
550
|
obs_render_main_texture();
|
|
551
|
+
|
|
552
|
+
// This is some AI code that would draw a rectangle (and works).
|
|
553
|
+
// Set projection and viewport
|
|
554
|
+
gs_ortho(0.0f, float(cx), 0.0f, float(cy), -100.0f, 100.0f);
|
|
555
|
+
gs_set_viewport(0, 0, cx, cy);
|
|
556
|
+
|
|
557
|
+
// This was me trying to understand what OSN does.
|
|
558
|
+
obs_scene_t* scene = obs_get_scene_by_name("WCR Scene");
|
|
559
|
+
obs_scene_enum_items(scene, draw_box, NULL);
|
|
560
|
+
obs_scene_release(scene);
|
|
379
561
|
}
|
|
380
562
|
|
|
381
563
|
void ObsInterface::initPreview(HWND parent) {
|
|
@@ -470,7 +652,8 @@ ObsInterface::ObsInterface(
|
|
|
470
652
|
const std::string& logPath,
|
|
471
653
|
const std::string& dataPath,
|
|
472
654
|
const std::string& recordingPath,
|
|
473
|
-
Napi::ThreadSafeFunction cb
|
|
655
|
+
Napi::ThreadSafeFunction cb,
|
|
656
|
+
bool buffering
|
|
474
657
|
) {
|
|
475
658
|
// Setup logs first so we have logs for the initialization.
|
|
476
659
|
base_set_log_handler(log_handler, (void*)logPath.c_str());
|
|
@@ -483,11 +666,11 @@ ObsInterface::ObsInterface(
|
|
|
483
666
|
jscb = cb;
|
|
484
667
|
|
|
485
668
|
// Create the resources we rely on.
|
|
486
|
-
|
|
669
|
+
create_output(recordingPath, buffering);
|
|
670
|
+
create_scene();
|
|
671
|
+
|
|
487
672
|
configure_video_encoder();
|
|
488
673
|
configure_audio_encoder();
|
|
489
|
-
configure_video_source();
|
|
490
|
-
configure_scene();
|
|
491
674
|
}
|
|
492
675
|
|
|
493
676
|
ObsInterface::~ObsInterface() {
|
|
@@ -557,26 +740,52 @@ void ObsInterface::startBuffering() {
|
|
|
557
740
|
|
|
558
741
|
void ObsInterface::startRecording(int offset) {
|
|
559
742
|
blog(LOG_INFO, "ObsInterface::startRecording enter");
|
|
560
|
-
bool is_active = obs_output_active(output);
|
|
561
743
|
|
|
562
|
-
|
|
563
|
-
blog(LOG_ERROR, "Buffer is not active");
|
|
564
|
-
throw std::runtime_error("Buffer is not active");
|
|
565
|
-
}
|
|
744
|
+
const char* type = obs_output_get_id(output);
|
|
566
745
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
calldata_init(&cd);
|
|
570
|
-
calldata_set_int(&cd, "offset_seconds", offset);
|
|
571
|
-
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
|
572
|
-
bool success = proc_handler_call(ph, "convert", &cd);
|
|
573
|
-
calldata_free(&cd);
|
|
746
|
+
if (strcmp(type, "replay_buffer") == 0) {
|
|
747
|
+
bool is_active = obs_output_active(output);
|
|
574
748
|
|
|
575
|
-
|
|
576
|
-
|
|
749
|
+
if (!is_active) {
|
|
750
|
+
blog(LOG_WARNING, "Buffer is not active");
|
|
751
|
+
throw std::runtime_error("Buffer is not active");
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
blog(LOG_INFO, "calling save proc handler");
|
|
755
|
+
calldata cd;
|
|
756
|
+
calldata_init(&cd);
|
|
757
|
+
calldata_set_int(&cd, "offset_seconds", offset);
|
|
758
|
+
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
|
759
|
+
bool success = proc_handler_call(ph, "convert", &cd);
|
|
760
|
+
calldata_free(&cd);
|
|
761
|
+
|
|
762
|
+
if (!success) {
|
|
763
|
+
throw std::runtime_error("Failed to call convert procedure handler");
|
|
764
|
+
}
|
|
765
|
+
} else if (strcmp(type, "ffmpeg_muxer") == 0) {
|
|
766
|
+
blog(LOG_INFO, "Starting ffmpeg_muxer output");
|
|
767
|
+
|
|
768
|
+
bool is_active = obs_output_active(output);
|
|
769
|
+
|
|
770
|
+
if (is_active) {
|
|
771
|
+
blog(LOG_WARNING, "Output already active");
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
blog(LOG_WARNING, "Call start");
|
|
776
|
+
bool success = obs_output_start(output);
|
|
777
|
+
|
|
778
|
+
if (!success) {
|
|
779
|
+
const char *err = obs_output_get_last_error(output);
|
|
780
|
+
blog(LOG_ERROR, "Failed to start recording: %s", err ? err : "Unknown error");
|
|
781
|
+
throw std::runtime_error("Failed to start recording");
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
blog(LOG_ERROR, "Unknown output type: %s", type);
|
|
785
|
+
throw std::runtime_error("Unknown output type!");
|
|
577
786
|
}
|
|
578
787
|
|
|
579
|
-
|
|
788
|
+
blog(LOG_INFO, "ObsInterface::startRecording exit");
|
|
580
789
|
}
|
|
581
790
|
|
|
582
791
|
void ObsInterface::stopRecording() {
|
|
@@ -597,10 +806,26 @@ std::string ObsInterface::getLastRecording() {
|
|
|
597
806
|
calldata cd;
|
|
598
807
|
calldata_init(&cd);
|
|
599
808
|
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
|
600
|
-
|
|
809
|
+
|
|
810
|
+
const char* type = obs_output_get_id(output);
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
if (strcmp(type, "ffmpeg_muxer") == 0) {
|
|
815
|
+
return recording_path;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
bool success;
|
|
819
|
+
|
|
820
|
+
if (strcmp(type, "replay_buffer") != 0) {
|
|
821
|
+
blog(LOG_ERROR, "Unknown output type: %s", type);
|
|
822
|
+
throw std::runtime_error("Unknown output type!");
|
|
823
|
+
}
|
|
601
824
|
|
|
602
825
|
if (!success) {
|
|
603
|
-
blog(LOG_ERROR, "Failed to call
|
|
826
|
+
blog(LOG_ERROR, "Failed to call procedure handler");
|
|
827
|
+
const char *err = obs_output_get_last_error(output);
|
|
828
|
+
blog(LOG_ERROR, "%s", err ? err : "Unknown error");
|
|
604
829
|
calldata_free(&cd);
|
|
605
830
|
return "";
|
|
606
831
|
}
|
|
@@ -613,6 +838,43 @@ std::string ObsInterface::getLastRecording() {
|
|
|
613
838
|
return path;
|
|
614
839
|
}
|
|
615
840
|
|
|
841
|
+
void ObsInterface::addSourceToScene(std::string name) {
|
|
842
|
+
blog(LOG_INFO, "ObsInterface::addSourceToScene called for source: %s", name.c_str());
|
|
843
|
+
|
|
844
|
+
obs_source_t *src = obs_get_source_by_name(name.c_str());
|
|
845
|
+
|
|
846
|
+
if (!src) {
|
|
847
|
+
blog(LOG_WARNING, "Did not find source for video source: %s", name.c_str());
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// TODO refuse to add twice?
|
|
852
|
+
|
|
853
|
+
obs_sceneitem_t *item = obs_scene_add(scene, src);
|
|
854
|
+
|
|
855
|
+
if (!item) {
|
|
856
|
+
blog(LOG_ERROR, "Failed to add source to scene: %s", name.c_str());
|
|
857
|
+
obs_source_release(src);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
blog(LOG_INFO, "ObsInterface::addSourceToScene exited");
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
void ObsInterface::removeSourceFromScene(std::string name) {
|
|
865
|
+
blog(LOG_INFO, "ObsInterface::removeSourceFromScene called for source: %s", name.c_str());
|
|
866
|
+
|
|
867
|
+
obs_sceneitem_t *item = obs_scene_find_source(scene, name.c_str());
|
|
868
|
+
|
|
869
|
+
if (!item) {
|
|
870
|
+
blog(LOG_WARNING, "Did not find scene item for video source: %s", name.c_str());
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
obs_sceneitem_remove(item);
|
|
875
|
+
blog(LOG_INFO, "ObsInterface::removeSourceFromScene exited");
|
|
876
|
+
}
|
|
877
|
+
|
|
616
878
|
void ObsInterface::getSourcePos(std::string name, vec2* pos, vec2* size, vec2* scale)
|
|
617
879
|
{
|
|
618
880
|
blog(LOG_INFO, "ObsInterface::getSourcePos called");
|
|
@@ -621,12 +883,12 @@ void ObsInterface::getSourcePos(std::string name, vec2* pos, vec2* size, vec2* s
|
|
|
621
883
|
obs_sceneitem_t *item = obs_scene_find_source(scene, name.c_str());
|
|
622
884
|
|
|
623
885
|
if (!src) {
|
|
624
|
-
blog(
|
|
886
|
+
blog(LOG_WARNING, "Did not find source for video source: %s", name);
|
|
625
887
|
return;
|
|
626
888
|
}
|
|
627
889
|
|
|
628
890
|
if (!item) {
|
|
629
|
-
blog(
|
|
891
|
+
blog(LOG_WARNING, "Did not find scene item for video source: %s", name);
|
|
630
892
|
return;
|
|
631
893
|
}
|
|
632
894
|
|
|
@@ -645,7 +907,7 @@ void ObsInterface::setSourcePos(std::string name, vec2* pos, vec2* scale) {
|
|
|
645
907
|
obs_sceneitem_t *item = obs_scene_find_source(scene, name.c_str());
|
|
646
908
|
|
|
647
909
|
if (!item) {
|
|
648
|
-
blog(
|
|
910
|
+
blog(LOG_WARNING, "Did not find scene item for video source: %s", name);
|
|
649
911
|
return;
|
|
650
912
|
}
|
|
651
913
|
|
package/src/obs_interface.h
CHANGED
|
@@ -10,26 +10,36 @@ struct SignalData {
|
|
|
10
10
|
class ObsInterface {
|
|
11
11
|
public:
|
|
12
12
|
ObsInterface(
|
|
13
|
-
const std::string& pluginPath, //
|
|
14
|
-
const std::string& logPath, //
|
|
15
|
-
const std::string& dataPath, //
|
|
16
|
-
const std::string& recordingPath, //
|
|
17
|
-
Napi::ThreadSafeFunction cb
|
|
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
|
+
bool buffering // Whether to enable buffering the recording in memory
|
|
18
19
|
);
|
|
19
20
|
|
|
20
21
|
~ObsInterface();
|
|
21
22
|
|
|
22
|
-
void startBuffering();
|
|
23
|
-
void startRecording(int offset);
|
|
24
|
-
void stopRecording();
|
|
25
|
-
std::string getLastRecording();
|
|
23
|
+
void startBuffering(); // Start buffering to memory.
|
|
24
|
+
void startRecording(int offset); // Convert the active buffered recording to a real one.
|
|
25
|
+
void stopRecording(); // Stop the recording.
|
|
26
|
+
std::string getLastRecording(); // Get the last recorded file path.
|
|
27
|
+
void updateRecordingDir(const std::string& recordingPath); // Output must not be active when calling this.
|
|
26
28
|
|
|
29
|
+
void createSource(std::string name, std::string type); // Create a new source
|
|
30
|
+
void deleteSource(std::string name); // Release a source.
|
|
31
|
+
obs_data_t* getSourceSettings(std::string name); // Get the current settings.
|
|
32
|
+
void setSourceSettings(std::string name, obs_data_t* settings); // Set settings.
|
|
33
|
+
obs_properties_t* getSourceProperties(std::string name); // Get the settings schema.
|
|
34
|
+
|
|
35
|
+
void addSourceToScene(std::string name); // Add source to scene.
|
|
36
|
+
void removeSourceFromScene(std::string name); // Remove source from scene.
|
|
27
37
|
void getSourcePos(std::string name, vec2* pos, vec2* size, vec2* scale); // Size is returned to allow clients to calculate scale.
|
|
28
38
|
void setSourcePos(std::string name, vec2* pos, vec2* scale); // Size does not get set here because it's set by the source itself.
|
|
29
39
|
|
|
30
40
|
void initPreview(HWND parent); // Must call this before showPreview to setup resources.
|
|
31
41
|
void showPreview(int x, int y, int width, int height); // Also used for moving and resizing.
|
|
32
|
-
void hidePreview();
|
|
42
|
+
void hidePreview(); // Hide the preview display.
|
|
33
43
|
|
|
34
44
|
std::vector<std::string> get_available_video_encoders();
|
|
35
45
|
|
|
@@ -50,6 +60,7 @@ class ObsInterface {
|
|
|
50
60
|
obs_display_t *display = nullptr;
|
|
51
61
|
HWND preview_hwnd = nullptr; // window handle for scene preview
|
|
52
62
|
Napi::ThreadSafeFunction jscb; // javascript callback
|
|
63
|
+
std::string recording_path = "";
|
|
53
64
|
|
|
54
65
|
void init_obs(const std::string& pluginPath, const std::string& dataPath);
|
|
55
66
|
void reset_video();
|
|
@@ -68,9 +79,9 @@ class ObsInterface {
|
|
|
68
79
|
void list_input_types();
|
|
69
80
|
void list_output_types();
|
|
70
81
|
|
|
71
|
-
void
|
|
82
|
+
void create_scene();
|
|
83
|
+
void create_output(const std::string& recordingPath, bool buffering);
|
|
84
|
+
|
|
72
85
|
void configure_video_encoder();
|
|
73
86
|
void configure_audio_encoder();
|
|
74
|
-
void configure_scene();
|
|
75
|
-
void configure_video_source();
|
|
76
87
|
};
|