noobs 0.0.89 → 0.0.130
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/data/obs-plugins/obs-filters/LUTs/black_and_white.png +0 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/grayscale.cube +32769 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/grayscale.png +0 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/invert.png +0 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/original.cube +33 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/original.png +0 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/posterize.png +0 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/red_isolated.png +0 -0
- package/dist/data/obs-plugins/obs-filters/LUTs/teal_lows_orange_highs.png +0 -0
- package/dist/data/obs-plugins/obs-filters/blend_add_filter.effect +54 -0
- package/dist/data/obs-plugins/obs-filters/blend_mul_filter.effect +54 -0
- package/dist/data/obs-plugins/obs-filters/blend_sub_filter.effect +54 -0
- package/dist/data/obs-plugins/obs-filters/chroma_key_filter.effect +99 -0
- package/dist/data/obs-plugins/obs-filters/chroma_key_filter_v2.effect +111 -0
- package/dist/data/obs-plugins/obs-filters/color.effect +95 -0
- package/dist/data/obs-plugins/obs-filters/color_correction_filter.effect +74 -0
- package/dist/data/obs-plugins/obs-filters/color_grade_filter.effect +177 -0
- package/dist/data/obs-plugins/obs-filters/color_key_filter.effect +65 -0
- package/dist/data/obs-plugins/obs-filters/color_key_filter_v2.effect +77 -0
- package/dist/data/obs-plugins/obs-filters/crop_filter.effect +95 -0
- package/dist/data/obs-plugins/obs-filters/hdr_tonemap_filter.effect +97 -0
- package/dist/data/obs-plugins/obs-filters/locale/af-ZA.ini +89 -0
- package/dist/data/obs-plugins/obs-filters/locale/ar-SA.ini +133 -0
- package/dist/data/obs-plugins/obs-filters/locale/az-AZ.ini +1 -0
- package/dist/data/obs-plugins/obs-filters/locale/ba-RU.ini +19 -0
- package/dist/data/obs-plugins/obs-filters/locale/be-BY.ini +134 -0
- package/dist/data/obs-plugins/obs-filters/locale/bg-BG.ini +71 -0
- package/dist/data/obs-plugins/obs-filters/locale/bn-BD.ini +106 -0
- package/dist/data/obs-plugins/obs-filters/locale/ca-ES.ini +128 -0
- package/dist/data/obs-plugins/obs-filters/locale/cs-CZ.ini +130 -0
- package/dist/data/obs-plugins/obs-filters/locale/da-DK.ini +119 -0
- package/dist/data/obs-plugins/obs-filters/locale/de-DE.ini +113 -0
- package/dist/data/obs-plugins/obs-filters/locale/el-GR.ini +128 -0
- package/dist/data/obs-plugins/obs-filters/locale/en-GB.ini +12 -0
- package/dist/data/obs-plugins/obs-filters/locale/en-US.ini +138 -0
- package/dist/data/obs-plugins/obs-filters/locale/eo-UY.ini +1 -0
- package/dist/data/obs-plugins/obs-filters/locale/es-ES.ini +129 -0
- package/dist/data/obs-plugins/obs-filters/locale/et-EE.ini +132 -0
- package/dist/data/obs-plugins/obs-filters/locale/eu-ES.ini +127 -0
- package/dist/data/obs-plugins/obs-filters/locale/fa-IR.ini +137 -0
- package/dist/data/obs-plugins/obs-filters/locale/fi-FI.ini +127 -0
- package/dist/data/obs-plugins/obs-filters/locale/fil-PH.ini +111 -0
- package/dist/data/obs-plugins/obs-filters/locale/fr-FR.ini +124 -0
- package/dist/data/obs-plugins/obs-filters/locale/gd-GB.ini +94 -0
- package/dist/data/obs-plugins/obs-filters/locale/gl-ES.ini +132 -0
- package/dist/data/obs-plugins/obs-filters/locale/he-IL.ini +135 -0
- package/dist/data/obs-plugins/obs-filters/locale/hi-IN.ini +134 -0
- package/dist/data/obs-plugins/obs-filters/locale/hr-HR.ini +80 -0
- package/dist/data/obs-plugins/obs-filters/locale/hu-HU.ini +131 -0
- package/dist/data/obs-plugins/obs-filters/locale/hy-AM.ini +130 -0
- package/dist/data/obs-plugins/obs-filters/locale/id-ID.ini +112 -0
- package/dist/data/obs-plugins/obs-filters/locale/it-IT.ini +130 -0
- package/dist/data/obs-plugins/obs-filters/locale/ja-JP.ini +134 -0
- package/dist/data/obs-plugins/obs-filters/locale/ka-GE.ini +133 -0
- package/dist/data/obs-plugins/obs-filters/locale/kaa.ini +41 -0
- package/dist/data/obs-plugins/obs-filters/locale/kab-KAB.ini +56 -0
- package/dist/data/obs-plugins/obs-filters/locale/kmr-TR.ini +126 -0
- package/dist/data/obs-plugins/obs-filters/locale/ko-KR.ini +134 -0
- package/dist/data/obs-plugins/obs-filters/locale/ms-MY.ini +131 -0
- package/dist/data/obs-plugins/obs-filters/locale/nb-NO.ini +115 -0
- package/dist/data/obs-plugins/obs-filters/locale/nl-NL.ini +111 -0
- package/dist/data/obs-plugins/obs-filters/locale/nn-NO.ini +19 -0
- package/dist/data/obs-plugins/obs-filters/locale/pl-PL.ini +127 -0
- package/dist/data/obs-plugins/obs-filters/locale/pt-BR.ini +128 -0
- package/dist/data/obs-plugins/obs-filters/locale/pt-PT.ini +130 -0
- package/dist/data/obs-plugins/obs-filters/locale/ro-RO.ini +125 -0
- package/dist/data/obs-plugins/obs-filters/locale/ru-RU.ini +135 -0
- package/dist/data/obs-plugins/obs-filters/locale/si-LK.ini +44 -0
- package/dist/data/obs-plugins/obs-filters/locale/sk-SK.ini +131 -0
- package/dist/data/obs-plugins/obs-filters/locale/sl-SI.ini +133 -0
- package/dist/data/obs-plugins/obs-filters/locale/sr-CS.ini +90 -0
- package/dist/data/obs-plugins/obs-filters/locale/sr-SP.ini +97 -0
- package/dist/data/obs-plugins/obs-filters/locale/sv-SE.ini +129 -0
- package/dist/data/obs-plugins/obs-filters/locale/szl-PL.ini +94 -0
- package/dist/data/obs-plugins/obs-filters/locale/ta-IN.ini +28 -0
- package/dist/data/obs-plugins/obs-filters/locale/th-TH.ini +132 -0
- package/dist/data/obs-plugins/obs-filters/locale/tl-PH.ini +64 -0
- package/dist/data/obs-plugins/obs-filters/locale/tr-TR.ini +129 -0
- package/dist/data/obs-plugins/obs-filters/locale/tt-RU.ini +27 -0
- package/dist/data/obs-plugins/obs-filters/locale/ug-CN.ini +133 -0
- package/dist/data/obs-plugins/obs-filters/locale/uk-UA.ini +135 -0
- package/dist/data/obs-plugins/obs-filters/locale/vi-VN.ini +131 -0
- package/dist/data/obs-plugins/obs-filters/locale/zh-CN.ini +138 -0
- package/dist/data/obs-plugins/obs-filters/locale/zh-TW.ini +136 -0
- package/dist/data/obs-plugins/obs-filters/luma_key_filter.effect +52 -0
- package/dist/data/obs-plugins/obs-filters/luma_key_filter_v2.effect +54 -0
- package/dist/data/obs-plugins/obs-filters/mask_alpha_filter.effect +54 -0
- package/dist/data/obs-plugins/obs-filters/mask_color_filter.effect +55 -0
- package/dist/data/obs-plugins/obs-filters/rtx_greenscreen.effect +183 -0
- package/dist/data/obs-plugins/obs-filters/sharpness.effect +76 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ar-SA.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/be-BY.ini +59 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/bg-BG.ini +2 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ca-ES.ini +59 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/cs-CZ.ini +55 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/da-DK.ini +54 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/de-DE.ini +55 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/en-GB.ini +9 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/en-US.ini +75 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/es-ES.ini +59 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/et-EE.ini +36 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/fa-IR.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/fi-FI.ini +59 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/fr-FR.ini +59 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/gl-ES.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/he-IL.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/hi-IN.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/hu-HU.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/id-ID.ini +55 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/is-IS.ini +2 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/it-IT.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ja-JP.ini +59 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ka-GE.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/kaa.ini +13 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/kmr-TR.ini +20 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ko-KR.ini +58 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ms-MY.ini +58 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/nb-NO.ini +24 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/nl-NL.ini +54 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/nn-NO.ini +1 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/pl-PL.ini +55 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/pt-BR.ini +58 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/pt-PT.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ru-RU.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/sk-SK.ini +56 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/sv-SE.ini +58 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/th-TH.ini +59 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/tr-TR.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/tt-RU.ini +2 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/ug-CN.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/uk-UA.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/vi-VN.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/zh-CN.ini +60 -0
- package/dist/data/obs-plugins/obs-nvenc/locale/zh-TW.ini +60 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/af-ZA.ini +19 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ar-SA.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ba-RU.ini +2 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/be-BY.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/bg-BG.ini +7 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/bn-BD.ini +8 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ca-ES.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/cs-CZ.ini +19 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/da-DK.ini +19 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/de-DE.ini +19 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/el-GR.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/en-GB.ini +1 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/en-US.ini +21 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/es-ES.ini +19 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/et-EE.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/eu-ES.ini +11 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/fa-IR.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/fi-FI.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/fil-PH.ini +11 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/fr-FR.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/gd-GB.ini +8 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/gl-ES.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/he-IL.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/hi-IN.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/hr-HR.ini +14 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/hu-HU.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/hy-AM.ini +13 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/id-ID.ini +18 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/is-IS.ini +1 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/it-IT.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ja-JP.ini +16 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ka-GE.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/kaa.ini +10 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/kab-KAB.ini +5 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/kmr-TR.ini +12 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ko-KR.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ms-MY.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/nb-NO.ini +16 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/nl-NL.ini +18 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/nn-NO.ini +2 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/oc-FR.ini +1 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/pl-PL.ini +16 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/pt-BR.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/pt-PT.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ro-RO.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ru-RU.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/si-LK.ini +8 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/sk-SK.ini +19 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/sl-SI.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/sr-CS.ini +6 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/sr-SP.ini +10 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/sv-SE.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ta-IN.ini +2 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/th-TH.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/tl-PH.ini +5 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/tr-TR.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/tt-RU.ini +3 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/ug-CN.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/uk-UA.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/vi-VN.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/zh-CN.ini +20 -0
- package/dist/data/obs-plugins/obs-qsv11/locale/zh-TW.ini +20 -0
- package/dist/data/obs-plugins/win-capture/get-graphics-offsets32.exe +0 -0
- package/dist/data/obs-plugins/win-capture/get-graphics-offsets64.exe +0 -0
- package/dist/data/obs-plugins/win-capture/graphics-hook32.dll +0 -0
- package/dist/data/obs-plugins/win-capture/graphics-hook64.dll +0 -0
- package/dist/data/obs-plugins/win-capture/inject-helper32.exe +0 -0
- package/dist/data/obs-plugins/win-capture/inject-helper64.exe +0 -0
- package/dist/noobs.node +0 -0
- package/dist/obs-plugins/image-source.dll +0 -0
- package/dist/obs-plugins/obs-filters.dll +0 -0
- package/dist/obs-plugins/obs-nvenc.dll +0 -0
- package/dist/obs-plugins/obs-qsv11.dll +0 -0
- package/dist/obs-plugins/obs-x264.dll +0 -0
- package/dist/obs-plugins/win-capture.dll +0 -0
- package/dist/obs-plugins/win-wasapi.dll +0 -0
- package/index.d.ts +24 -18
- package/package.json +1 -1
- package/src/main.cpp +215 -33
- package/src/obs_interface.cpp +598 -288
- package/src/obs_interface.h +85 -38
package/src/obs_interface.cpp
CHANGED
|
@@ -8,46 +8,34 @@
|
|
|
8
8
|
#include <graphics/vec4.h>
|
|
9
9
|
#include <util/platform.h>
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
while (obs_enum_encoder_types(idx++, &encoder_type)) {
|
|
18
|
-
bool video = obs_get_encoder_type(encoder_type) == OBS_ENCODER_VIDEO;
|
|
11
|
+
void call_jscb(Napi::Env env, Napi::Function cb, SignalData* sd) {
|
|
12
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
13
|
+
obj.Set("type", Napi::String::New(env, sd->type));
|
|
14
|
+
obj.Set("id", Napi::String::New(env, sd->id));
|
|
15
|
+
obj.Set("code", Napi::Number::New(env, sd->code));
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
if (sd->value.has_value()) {
|
|
18
|
+
obj.Set("value", Napi::Number::New(env, sd->value.value()));
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
cb.Call({ obj });
|
|
22
|
+
delete sd;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
void ObsInterface::list_encoders(obs_encoder_type type)
|
|
28
26
|
{
|
|
29
|
-
blog(LOG_INFO, "
|
|
30
|
-
blog(LOG_INFO, "List encoders of type: %d", type);
|
|
31
|
-
|
|
27
|
+
blog(LOG_INFO, "Encoders:");
|
|
32
28
|
size_t idx = 0;
|
|
33
29
|
const char *encoder_type;
|
|
34
30
|
|
|
35
31
|
while (obs_enum_encoder_types(idx++, &encoder_type)) {
|
|
36
|
-
if (obs_get_encoder_type(encoder_type) != type) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// if (obs_get_encoder_caps(encoder_type) & hide_flags) {
|
|
41
|
-
// continue;
|
|
42
|
-
// }
|
|
43
|
-
|
|
44
32
|
blog(LOG_INFO, "\t- %s (%s)", encoder_type, obs_encoder_get_display_name(encoder_type));
|
|
45
33
|
}
|
|
46
34
|
};
|
|
47
35
|
|
|
48
36
|
void ObsInterface::list_source_types()
|
|
49
37
|
{
|
|
50
|
-
blog(LOG_INFO, "
|
|
38
|
+
blog(LOG_INFO, "Sources:");
|
|
51
39
|
size_t idx = 0;
|
|
52
40
|
const char *src = nullptr;
|
|
53
41
|
|
|
@@ -56,20 +44,9 @@ void ObsInterface::list_source_types()
|
|
|
56
44
|
}
|
|
57
45
|
}
|
|
58
46
|
|
|
59
|
-
void ObsInterface::list_input_types()
|
|
60
|
-
{
|
|
61
|
-
blog(LOG_INFO, "List input types");
|
|
62
|
-
size_t idx = 0;
|
|
63
|
-
const char *src = nullptr;
|
|
64
|
-
|
|
65
|
-
while (obs_enum_input_types(idx++, &src)) {
|
|
66
|
-
blog(LOG_INFO, "\t- %s", src);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
47
|
void ObsInterface::list_output_types()
|
|
71
48
|
{
|
|
72
|
-
blog(LOG_INFO, "
|
|
49
|
+
blog(LOG_INFO, "Outputs:");
|
|
73
50
|
size_t idx = 0;
|
|
74
51
|
const char *src = nullptr;
|
|
75
52
|
|
|
@@ -78,9 +55,10 @@ void ObsInterface::list_output_types()
|
|
|
78
55
|
}
|
|
79
56
|
}
|
|
80
57
|
|
|
81
|
-
void ObsInterface::load_module(const char* module, const char* data) {
|
|
58
|
+
void ObsInterface::load_module(const char* module, const char* data, bool allowFail) {
|
|
82
59
|
blog(LOG_INFO, "Loading module: %s", module);
|
|
83
60
|
blog(LOG_INFO, "Data path: %s", data);
|
|
61
|
+
blog(LOG_INFO, "Allow fail: %d", allowFail);
|
|
84
62
|
|
|
85
63
|
obs_module_t *ptr = NULL;
|
|
86
64
|
int success = obs_open_module(&ptr, module, data);
|
|
@@ -92,22 +70,60 @@ void ObsInterface::load_module(const char* module, const char* data) {
|
|
|
92
70
|
|
|
93
71
|
bool initmod = obs_init_module(ptr);
|
|
94
72
|
|
|
95
|
-
if (
|
|
96
|
-
blog(
|
|
73
|
+
if (initmod) {
|
|
74
|
+
blog(LOG_INFO, "Module initialized successfully!");
|
|
75
|
+
} else if (allowFail) {
|
|
76
|
+
blog(LOG_INFO, "Module initialization failed, but allowed to fail: %s", module);
|
|
77
|
+
} else {
|
|
78
|
+
blog(LOG_ERROR, "Failed to initialize module: %s", module);
|
|
97
79
|
throw std::runtime_error("Module initialization failed!");
|
|
98
80
|
}
|
|
99
81
|
}
|
|
100
82
|
|
|
101
|
-
void ObsInterface::
|
|
102
|
-
blog(LOG_INFO, "
|
|
83
|
+
void ObsInterface::setVideoContext(int fps, int width, int height) {
|
|
84
|
+
blog(LOG_INFO, "Reset video context");
|
|
85
|
+
|
|
86
|
+
blog(LOG_INFO, "FPS: %d", fps);
|
|
87
|
+
blog(LOG_INFO, "Width: %d", width);
|
|
88
|
+
blog(LOG_INFO, "Height: %d", height);
|
|
89
|
+
|
|
90
|
+
if (fps <= 10) {
|
|
91
|
+
blog(LOG_WARNING, "Invalid FPS provided for reset, using default 10");
|
|
92
|
+
fps = 60;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (width <= 32 || height <= 32) {
|
|
96
|
+
blog(LOG_WARNING, "Invalid width or height provided for reset, using default 1920x1080");
|
|
97
|
+
width = 1920;
|
|
98
|
+
height = 1080;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
int ret = reset_video(fps, width, height);
|
|
102
|
+
|
|
103
|
+
if (ret == OBS_VIDEO_CURRENTLY_ACTIVE) {
|
|
104
|
+
blog(LOG_WARNING, "Can't reset video as currently active");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (ret != OBS_VIDEO_SUCCESS) {
|
|
109
|
+
blog(LOG_ERROR, "Failed to reset video context: %d", ret);
|
|
110
|
+
throw std::runtime_error("Failed to reset video context");
|
|
111
|
+
}
|
|
103
112
|
|
|
113
|
+
// Recreate the encoders as they are tied to the video context.
|
|
114
|
+
create_video_encoders();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
int ObsInterface::reset_video(int fps, int width, int height) {
|
|
119
|
+
blog(LOG_INFO, "Reset video");
|
|
104
120
|
obs_video_info ovi = {};
|
|
105
121
|
|
|
106
|
-
ovi.base_width =
|
|
107
|
-
ovi.base_height =
|
|
108
|
-
ovi.output_width =
|
|
109
|
-
ovi.output_height =
|
|
110
|
-
ovi.fps_num =
|
|
122
|
+
ovi.base_width = width;
|
|
123
|
+
ovi.base_height = height;
|
|
124
|
+
ovi.output_width = width;
|
|
125
|
+
ovi.output_height = height;
|
|
126
|
+
ovi.fps_num = fps;
|
|
111
127
|
ovi.fps_den = 1;
|
|
112
128
|
|
|
113
129
|
ovi.output_format = VIDEO_FORMAT_NV12;
|
|
@@ -118,29 +134,14 @@ void ObsInterface::reset_video() {
|
|
|
118
134
|
ovi.gpu_conversion = true;
|
|
119
135
|
ovi.graphics_module = "libobs-d3d11.dll";
|
|
120
136
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
obs_enter_graphics();
|
|
124
|
-
int dt = gs_get_device_type();
|
|
125
|
-
blog(LOG_INFO, "Device type = %d", dt); // should be 1 for D3D11
|
|
126
|
-
obs_leave_graphics();
|
|
127
|
-
|
|
128
|
-
if (success != OBS_VIDEO_SUCCESS) {
|
|
129
|
-
blog(LOG_ERROR, "Failed to reset video!");
|
|
130
|
-
throw std::runtime_error("Failed to reset video!");
|
|
131
|
-
}
|
|
137
|
+
return obs_reset_video(&ovi);
|
|
132
138
|
}
|
|
133
139
|
|
|
134
|
-
|
|
140
|
+
bool ObsInterface::reset_audio() {
|
|
135
141
|
struct obs_audio_info oai = {0};
|
|
136
142
|
oai.samples_per_sec = 48000;
|
|
137
143
|
oai.speakers = SPEAKERS_STEREO;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!reset) {
|
|
141
|
-
blog(LOG_ERROR, "Failed to reset audio!");
|
|
142
|
-
throw std::runtime_error("Failed to reset audio!");
|
|
143
|
-
}
|
|
144
|
+
return obs_reset_audio(&oai);
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
void ObsInterface::init_obs(const std::string& distPath) {
|
|
@@ -166,10 +167,12 @@ void ObsInterface::init_obs(const std::string& distPath) {
|
|
|
166
167
|
|
|
167
168
|
std::string effectsPath = basePath + "data/effects/";
|
|
168
169
|
std::string pluginPath = basePath + "obs-plugins/";
|
|
170
|
+
std::string pluginDataPath = basePath + "data/obs-plugins/";
|
|
169
171
|
|
|
170
172
|
blog(LOG_INFO, "Base path: %s", basePath.c_str());
|
|
171
173
|
blog(LOG_INFO, "Effects path: %s", effectsPath.c_str());
|
|
172
174
|
blog(LOG_INFO, "Plugin path: %s", pluginPath.c_str());
|
|
175
|
+
blog(LOG_INFO, "Data path: %s", pluginDataPath.c_str());
|
|
173
176
|
|
|
174
177
|
// Add the effects path. We need this before resetting video and audio
|
|
175
178
|
// to ensure the effects are available. The function is deprecated in
|
|
@@ -177,28 +180,43 @@ void ObsInterface::init_obs(const std::string& distPath) {
|
|
|
177
180
|
obs_add_data_path(effectsPath.c_str());
|
|
178
181
|
|
|
179
182
|
// This must come before loading modules to initialize D3D11.
|
|
180
|
-
|
|
181
|
-
|
|
183
|
+
// Choose some sensible defaults that can be reconfigured.
|
|
184
|
+
int rc = reset_video(60, 1920, 1080);
|
|
185
|
+
|
|
186
|
+
if (rc != OBS_VIDEO_SUCCESS) {
|
|
187
|
+
blog(LOG_ERROR, "Failed to reset video!");
|
|
188
|
+
throw std::runtime_error("Failed to reset video!");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!reset_audio()) {
|
|
192
|
+
blog(LOG_ERROR, "Failed to reset audio!");
|
|
193
|
+
throw std::runtime_error("Failed to reset audio!");
|
|
194
|
+
}
|
|
182
195
|
|
|
183
196
|
std::vector<std::string> modules = {
|
|
184
|
-
"obs-x264",
|
|
185
|
-
"obs-ffmpeg",
|
|
197
|
+
"obs-x264", // Software encoder.
|
|
198
|
+
"obs-ffmpeg", // Contains AMF (AMD) encoder support.
|
|
186
199
|
"win-capture", // Required for basically all forms of capture on Windows.
|
|
187
200
|
"image-source", // Required for image sources.
|
|
188
|
-
"win-wasapi"
|
|
201
|
+
"win-wasapi", // Required for WASAPI audio input.
|
|
202
|
+
"obs-nvenc", // Required for NVENC video encoding.
|
|
203
|
+
"obs-qsv11", // Required for QSV video encoding.
|
|
204
|
+
"obs-filters" // Required for audio filters.
|
|
189
205
|
};
|
|
190
206
|
|
|
191
207
|
for (const auto& module : modules) {
|
|
192
208
|
std::string modulePath = pluginPath + module + ".dll";
|
|
193
|
-
std::string moduleDataPath =
|
|
194
|
-
|
|
209
|
+
std::string moduleDataPath = pluginDataPath + module;
|
|
210
|
+
|
|
211
|
+
// NVENC fails if there is no NVENC hardware support.
|
|
212
|
+
bool allowFail = module == "obs-nvenc";
|
|
213
|
+
load_module(modulePath.c_str(), moduleDataPath.c_str(), allowFail);
|
|
195
214
|
}
|
|
196
215
|
|
|
197
216
|
obs_post_load_modules();
|
|
198
217
|
|
|
199
218
|
list_encoders();
|
|
200
219
|
list_source_types();
|
|
201
|
-
list_input_types();
|
|
202
220
|
list_output_types();
|
|
203
221
|
|
|
204
222
|
blog(LOG_INFO, "Exit init_obs");
|
|
@@ -207,158 +225,119 @@ void ObsInterface::init_obs(const std::string& distPath) {
|
|
|
207
225
|
void ObsInterface::create_output() {
|
|
208
226
|
blog(LOG_INFO, "Create outputs");
|
|
209
227
|
|
|
210
|
-
|
|
211
|
-
|
|
228
|
+
const char* type = buffering ? "replay_buffer" : "ffmpeg_muxer";
|
|
229
|
+
const char* name = buffering ? "Buffer Output" : "File Output";
|
|
212
230
|
|
|
213
|
-
if (
|
|
214
|
-
blog(
|
|
215
|
-
|
|
231
|
+
if (output) {
|
|
232
|
+
blog(LOG_DEBUG, "Releasing existing output");
|
|
233
|
+
obs_output_release(output);
|
|
216
234
|
}
|
|
217
235
|
|
|
218
|
-
blog(LOG_INFO, "Creating
|
|
219
|
-
|
|
236
|
+
blog(LOG_INFO, "Creating replay buffer output");
|
|
237
|
+
output = obs_output_create(type, name, NULL, NULL);
|
|
220
238
|
|
|
221
|
-
if (!
|
|
222
|
-
blog(LOG_ERROR, "Failed to create
|
|
223
|
-
throw std::runtime_error("Failed to create
|
|
239
|
+
if (!output) {
|
|
240
|
+
blog(LOG_ERROR, "Failed to create output!");
|
|
241
|
+
throw std::runtime_error("Failed to create output!");
|
|
224
242
|
}
|
|
225
243
|
|
|
226
|
-
obs_data_t *
|
|
227
|
-
blog(LOG_INFO, "Set replay_buffer settings");
|
|
228
|
-
obs_data_set_int(buffer_settings, "max_time_sec", 60);
|
|
229
|
-
obs_data_set_int(buffer_settings, "max_size_mb", 1024);
|
|
230
|
-
obs_data_set_string(buffer_settings, "directory", recording_path.c_str());
|
|
231
|
-
obs_data_set_string(buffer_settings, "format", "%CCYY-%MM-%DD %hh-%mm-%ss");
|
|
232
|
-
obs_data_set_string(buffer_settings, "extension", "mp4");
|
|
233
|
-
obs_output_update(buffer_output, buffer_settings);
|
|
234
|
-
obs_data_release(buffer_settings);
|
|
244
|
+
obs_data_t *settings = obs_data_create();
|
|
235
245
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
+
if (buffering) {
|
|
247
|
+
blog(LOG_INFO, "Set replay buffer settings");
|
|
248
|
+
obs_data_set_int(settings, "max_time_sec", 60);
|
|
249
|
+
obs_data_set_int(settings, "max_size_mb", 1024);
|
|
250
|
+
obs_data_set_string(settings, "directory", recording_path.c_str());
|
|
251
|
+
obs_data_set_string(settings, "format", "%CCYY-%MM-%DD %hh-%mm-%ss");
|
|
252
|
+
obs_data_set_string(settings, "extension", "mp4");
|
|
253
|
+
} else {
|
|
254
|
+
blog(LOG_INFO, "Set ffmpeg_muxer settings");
|
|
255
|
+
// Need to specify the exact path for ffmpeg_muxer. We will write this again at start recording.
|
|
256
|
+
std::string filename = recording_path + "\\" + get_current_date_time() + ".mp4";
|
|
257
|
+
obs_data_set_string(settings, "path", filename.c_str());
|
|
258
|
+
unbuffered_output_filename = filename;
|
|
259
|
+
}
|
|
246
260
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
connect_signal_handlers(
|
|
261
|
+
obs_output_update(output, settings);
|
|
262
|
+
obs_data_release(settings);
|
|
263
|
+
connect_signal_handlers(output);
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
void ObsInterface::setRecordingDir(const std::string& recordingPath) {
|
|
253
|
-
blog(LOG_INFO, "Set recording directory");
|
|
254
|
-
// TODO make this work for file output also.
|
|
255
|
-
|
|
256
|
-
obs_output_t *output = buffering ? buffer_output : file_output;
|
|
257
|
-
|
|
258
|
-
if (!output) {
|
|
259
|
-
blog(LOG_ERROR, "No output to update recording directory");
|
|
260
|
-
throw std::runtime_error("Output not initialized");
|
|
261
|
-
}
|
|
267
|
+
blog(LOG_INFO, "Set recording directory. Path: %s", recordingPath.c_str());
|
|
262
268
|
|
|
263
|
-
// check its not active
|
|
264
269
|
if (obs_output_active(output)) {
|
|
265
270
|
blog(LOG_ERROR, "Output is active, cannot update recording path");
|
|
266
271
|
throw std::runtime_error("Output is active, cannot update recording path");
|
|
267
272
|
}
|
|
268
273
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (!settings) {
|
|
272
|
-
blog(LOG_ERROR, "Failed to get output settings");
|
|
273
|
-
throw std::runtime_error("Failed to get output settings");
|
|
274
|
-
}
|
|
274
|
+
recording_path = recordingPath;
|
|
275
|
+
create_output();
|
|
275
276
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
obs_data_release(settings);
|
|
277
|
+
create_video_encoders();
|
|
278
|
+
create_audio_encoders();
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
void ObsInterface::create_video_encoders() {
|
|
282
|
-
blog(LOG_INFO, "
|
|
282
|
+
blog(LOG_INFO, "Set video encoder: %s", video_encoder_id.c_str());
|
|
283
283
|
|
|
284
|
-
|
|
284
|
+
if (video_encoder) {
|
|
285
|
+
blog(LOG_DEBUG, "Releasing file video encoder");
|
|
286
|
+
obs_encoder_release(video_encoder);
|
|
287
|
+
video_encoder = nullptr;
|
|
288
|
+
}
|
|
285
289
|
|
|
290
|
+
video_encoder = obs_video_encoder_create(
|
|
291
|
+
video_encoder_id.c_str(),
|
|
292
|
+
"noobs_file_encoder",
|
|
293
|
+
video_encoder_settings,
|
|
294
|
+
NULL
|
|
295
|
+
);
|
|
286
296
|
|
|
287
|
-
if (!
|
|
297
|
+
if (!video_encoder) {
|
|
288
298
|
blog(LOG_ERROR, "Failed to create video encoder!");
|
|
289
299
|
throw std::runtime_error("Failed to create video encoder!");
|
|
290
300
|
}
|
|
291
301
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (!buffer_video_encoder) {
|
|
295
|
-
blog(LOG_ERROR, "Failed to create buffer video encoder!");
|
|
296
|
-
throw std::runtime_error("Failed to create buffer video encoder!");
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
blog(LOG_INFO, "Set file video encoder settings");
|
|
300
|
-
obs_data_t* venc_settings = obs_data_create();
|
|
301
|
-
// obs_data_set_string(venc_settings, "preset", "speed"); // Faster preset
|
|
302
|
-
obs_data_set_string(venc_settings, "rate_control", "CRF");
|
|
303
|
-
obs_data_set_int(venc_settings, "crf", 22);
|
|
304
|
-
obs_data_set_string(venc_settings, "profile", "main");
|
|
305
|
-
obs_data_set_int(venc_settings, "keyint_sec", 1); // Set keyframe interval to 1 second
|
|
306
|
-
|
|
307
|
-
obs_encoder_update(file_video_encoder, venc_settings);
|
|
308
|
-
obs_encoder_update(buffer_video_encoder, venc_settings);
|
|
309
|
-
obs_data_release(venc_settings);
|
|
310
|
-
|
|
311
|
-
obs_output_set_video_encoder(file_output, file_video_encoder);
|
|
312
|
-
obs_encoder_set_video(file_video_encoder, obs_get_video());
|
|
313
|
-
obs_output_set_video_encoder(buffer_output, buffer_video_encoder);
|
|
314
|
-
obs_encoder_set_video(buffer_video_encoder, obs_get_video());
|
|
302
|
+
obs_output_set_video_encoder(output, video_encoder);
|
|
303
|
+
obs_encoder_set_video(video_encoder, obs_get_video());
|
|
315
304
|
}
|
|
316
305
|
|
|
317
306
|
void ObsInterface::create_audio_encoders() {
|
|
318
307
|
blog(LOG_INFO, "Create audio encoder");
|
|
319
308
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// if (audio_encoder) {
|
|
326
|
-
// blog(LOG_DEBUG, "Releasing audio encoder");
|
|
327
|
-
// obs_encoder_release(audio_encoder);
|
|
328
|
-
// audio_encoder = nullptr;
|
|
329
|
-
// }
|
|
309
|
+
if (audio_encoder) {
|
|
310
|
+
blog(LOG_DEBUG, "Releasing audio encoder");
|
|
311
|
+
obs_encoder_release(audio_encoder);
|
|
312
|
+
audio_encoder = nullptr;
|
|
313
|
+
}
|
|
330
314
|
|
|
331
|
-
|
|
315
|
+
audio_encoder = obs_audio_encoder_create(
|
|
316
|
+
"ffmpeg_aac",
|
|
317
|
+
"aac_file",
|
|
318
|
+
NULL,
|
|
319
|
+
0,
|
|
320
|
+
NULL
|
|
321
|
+
);
|
|
332
322
|
|
|
333
|
-
if (!
|
|
323
|
+
if (!audio_encoder) {
|
|
334
324
|
blog(LOG_ERROR, "Failed to create audio encoder!");
|
|
335
325
|
throw std::runtime_error("Failed to create audio encoder!");
|
|
336
326
|
}
|
|
337
327
|
|
|
338
|
-
buffer_audio_encoder = obs_audio_encoder_create("ffmpeg_aac", "aac_buffer", NULL, 0, NULL);
|
|
339
|
-
|
|
340
|
-
if (!buffer_audio_encoder) {
|
|
341
|
-
blog(LOG_ERROR, "Failed to create buffer audio encoder!");
|
|
342
|
-
throw std::runtime_error("Failed to create buffer audio encoder!");
|
|
343
|
-
}
|
|
344
|
-
|
|
345
328
|
blog(LOG_INFO, "Set audio encoder settings");
|
|
346
329
|
obs_data_t *aenc_settings = obs_data_create();
|
|
347
330
|
obs_data_set_int(aenc_settings, "bitrate", 128);
|
|
348
|
-
obs_encoder_update(
|
|
349
|
-
obs_encoder_update(buffer_audio_encoder, aenc_settings);
|
|
331
|
+
obs_encoder_update(audio_encoder, aenc_settings);
|
|
350
332
|
obs_data_release(aenc_settings);
|
|
351
333
|
|
|
352
|
-
obs_output_set_audio_encoder(
|
|
353
|
-
obs_encoder_set_audio(
|
|
354
|
-
|
|
355
|
-
obs_output_set_audio_encoder(buffer_output, buffer_audio_encoder, 0);
|
|
356
|
-
obs_encoder_set_audio(buffer_audio_encoder, obs_get_audio());
|
|
334
|
+
obs_output_set_audio_encoder(output, audio_encoder, 0);
|
|
335
|
+
obs_encoder_set_audio(audio_encoder, obs_get_audio());
|
|
357
336
|
}
|
|
358
337
|
|
|
359
338
|
void ObsInterface::create_scene() {
|
|
360
339
|
blog(LOG_INFO, "Create scene");
|
|
361
|
-
scene = obs_scene_create("
|
|
340
|
+
scene = obs_scene_create("Base Scene");
|
|
362
341
|
|
|
363
342
|
if (!scene) {
|
|
364
343
|
blog(LOG_ERROR, "Failed to create scene!");
|
|
@@ -375,12 +354,34 @@ void ObsInterface::create_scene() {
|
|
|
375
354
|
obs_set_output_source(0, scene_source); // 0 = video track
|
|
376
355
|
}
|
|
377
356
|
|
|
378
|
-
void ObsInterface::
|
|
357
|
+
void ObsInterface::volmeter_callback(void *data,
|
|
358
|
+
const float magnitude[MAX_AUDIO_CHANNELS],
|
|
359
|
+
const float peak[MAX_AUDIO_CHANNELS],
|
|
360
|
+
const float inputPeak[MAX_AUDIO_CHANNELS])
|
|
361
|
+
{
|
|
362
|
+
// blog(LOG_DEBUG, "Volmeter callback triggered: %f %f %f",
|
|
363
|
+
// obs_db_to_mul(magnitude[0]),
|
|
364
|
+
// obs_db_to_mul(peak[0]),
|
|
365
|
+
// obs_db_to_mul(inputPeak[0])
|
|
366
|
+
// );
|
|
367
|
+
|
|
368
|
+
SignalContext* ctx = static_cast<SignalContext*>(data);
|
|
369
|
+
ObsInterface* self = ctx->self;
|
|
370
|
+
|
|
371
|
+
if (!self->volmeter_enabled) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
SignalData* sd = new SignalData{ "volmeter", ctx->id.c_str(), 0, obs_db_to_mul(peak[0]) };
|
|
376
|
+
self->jscb.NonBlockingCall(sd, call_jscb);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
std::string ObsInterface::createSource(std::string name, std::string type) {
|
|
379
380
|
blog(LOG_INFO, "Create source: %s of type %s", name.c_str(), type.c_str());
|
|
380
381
|
|
|
381
382
|
obs_source_t *source = obs_source_create(
|
|
382
|
-
type.c_str(),
|
|
383
|
-
name.c_str(),
|
|
383
|
+
type.c_str(), // Type of source, e.g. "wasapi_input_capture"
|
|
384
|
+
name.c_str(), // Name of the source, e.g. "My Audio Input"
|
|
384
385
|
NULL, // No settings.
|
|
385
386
|
NULL // No hotkey data.
|
|
386
387
|
);
|
|
@@ -390,23 +391,102 @@ void ObsInterface::createSource(std::string name, std::string type) {
|
|
|
390
391
|
throw std::runtime_error("Failed to create source!");
|
|
391
392
|
}
|
|
392
393
|
|
|
394
|
+
// The name might not match what we asked for if there is a duplicate.
|
|
395
|
+
// So pass it back to the client to avoid potential for a mismatch.
|
|
396
|
+
std::string real_name = obs_source_get_name(source);
|
|
397
|
+
|
|
398
|
+
if (type == AUDIO_OUTPUT || type == AUDIO_INPUT || type == AUDIO_PROCESS) {
|
|
399
|
+
blog(LOG_INFO, "Creating volmeter for source: %s", real_name.c_str());
|
|
400
|
+
|
|
401
|
+
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_CUBIC);
|
|
402
|
+
obs_volmeter_attach_source(volmeter, source);
|
|
403
|
+
|
|
404
|
+
SignalContext* ctx = new SignalContext{ this, real_name }; // TODO don't leak this.
|
|
405
|
+
obs_volmeter_add_callback(volmeter, volmeter_callback, ctx);
|
|
406
|
+
|
|
407
|
+
// Store the volmeter in the volmeters map.
|
|
408
|
+
volmeters[real_name] = volmeter;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (type == AUDIO_INPUT && force_mono) {
|
|
412
|
+
blog(LOG_INFO, "Setting force mono for new source: %s", real_name.c_str());
|
|
413
|
+
uint32_t flags = obs_source_get_flags(source);
|
|
414
|
+
obs_source_set_flags(source, flags | OBS_SOURCE_FLAG_FORCE_MONO);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (type == AUDIO_INPUT && audio_suppression) {
|
|
418
|
+
blog(LOG_INFO, "Setting up filter for new source: %s", real_name.c_str());
|
|
419
|
+
std::string filter_name = "Filter for " + real_name;
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
obs_source_t *filter = obs_source_create(
|
|
423
|
+
"noise_suppress_filter_v2",
|
|
424
|
+
filter_name.c_str(),
|
|
425
|
+
nullptr, // Defaults are sensible.
|
|
426
|
+
nullptr
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
if (!filter) {
|
|
430
|
+
blog(LOG_ERROR, "Failed to create filter for source: %s", real_name.c_str());
|
|
431
|
+
throw std::runtime_error("Failed to create filter!");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
filters[real_name] = filter;
|
|
435
|
+
obs_source_filter_add(source, filter);
|
|
436
|
+
}
|
|
437
|
+
|
|
393
438
|
// Store the source in the sources map.
|
|
394
|
-
sources[
|
|
439
|
+
sources[real_name] = source;
|
|
440
|
+
|
|
441
|
+
// Store the dimensions so we can fire a callback if they change.
|
|
442
|
+
uint32_t w = obs_source_get_width(source);
|
|
443
|
+
uint32_t h = obs_source_get_height(source);
|
|
444
|
+
sizes[real_name] = { w, h };
|
|
445
|
+
|
|
446
|
+
return real_name;
|
|
395
447
|
}
|
|
396
448
|
|
|
397
449
|
void ObsInterface::deleteSource(std::string name) {
|
|
398
450
|
blog(LOG_INFO, "Delete source: %s", name.c_str());
|
|
399
451
|
|
|
452
|
+
// First release a volmeter if there is one present.
|
|
453
|
+
// Only audio sources have volmeters ofcourse.
|
|
454
|
+
auto vol_it = volmeters.find(name);
|
|
455
|
+
|
|
456
|
+
if (vol_it != volmeters.end()) {
|
|
457
|
+
obs_volmeter_t* volmeter = vol_it->second;
|
|
458
|
+
obs_volmeter_remove_callback(volmeter, volmeter_callback, this);
|
|
459
|
+
obs_volmeter_detach_source(volmeter);
|
|
460
|
+
obs_volmeter_destroy(volmeter);
|
|
461
|
+
blog(LOG_INFO, "Volmeter deleted for source: %s", name.c_str());
|
|
462
|
+
volmeters.erase(name);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Now deal with the source itself.
|
|
400
466
|
auto it = sources.find(name);
|
|
401
467
|
|
|
402
468
|
if (it == sources.end()) {
|
|
403
|
-
blog(LOG_WARNING, "Source %s not found", name.c_str());
|
|
469
|
+
blog(LOG_WARNING, "Source %s not found when deleting", name.c_str());
|
|
404
470
|
return;
|
|
405
471
|
}
|
|
406
472
|
|
|
407
473
|
obs_source_t* source = it->second;
|
|
474
|
+
|
|
475
|
+
// Remove and release any filters.
|
|
476
|
+
auto filter_it = filters.find(name);
|
|
477
|
+
|
|
478
|
+
if (filter_it != filters.end()) {
|
|
479
|
+
obs_source_t* filter = filter_it->second;
|
|
480
|
+
obs_source_filter_remove(source, filter);
|
|
481
|
+
obs_source_release(filter);
|
|
482
|
+
filters.erase(name);
|
|
483
|
+
blog(LOG_INFO, "Filter deleted for source: %s", name.c_str());
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
obs_source_remove(source); // ???
|
|
408
487
|
obs_source_release(source);
|
|
409
488
|
sources.erase(name);
|
|
489
|
+
sizes.erase(name);
|
|
410
490
|
blog(LOG_INFO, "Source deleted: %s", name.c_str());
|
|
411
491
|
}
|
|
412
492
|
|
|
@@ -416,7 +496,7 @@ obs_data_t* ObsInterface::getSourceSettings(std::string name) {
|
|
|
416
496
|
auto it = sources.find(name);
|
|
417
497
|
|
|
418
498
|
if (it == sources.end()) {
|
|
419
|
-
blog(LOG_WARNING, "Source %s not found", name.c_str());
|
|
499
|
+
blog(LOG_WARNING, "Source %s not found when getting settings", name.c_str());
|
|
420
500
|
throw std::runtime_error("Source not found!");
|
|
421
501
|
}
|
|
422
502
|
|
|
@@ -437,13 +517,27 @@ void ObsInterface::setSourceSettings(std::string name, obs_data_t* settings) {
|
|
|
437
517
|
auto it = sources.find(name);
|
|
438
518
|
|
|
439
519
|
if (it == sources.end()) {
|
|
440
|
-
blog(LOG_WARNING, "Source %s not found", name.c_str());
|
|
520
|
+
blog(LOG_WARNING, "Source %s not found when setting settings", name.c_str());
|
|
441
521
|
throw std::runtime_error("Source not found!");
|
|
442
522
|
}
|
|
443
523
|
|
|
444
524
|
obs_source_t* source = it->second;
|
|
445
|
-
|
|
446
525
|
obs_source_update(source, settings);
|
|
526
|
+
|
|
527
|
+
// If this is an audio source, it may have an attached volmeter.
|
|
528
|
+
auto vol_it = volmeters.find(name);
|
|
529
|
+
|
|
530
|
+
if (vol_it != volmeters.end()) {
|
|
531
|
+
// Rebind it. This avoids leaving it attached to stale audio stream
|
|
532
|
+
// in the event of a device change.
|
|
533
|
+
blog(LOG_INFO, "Rebinding volmeter for source: %s", name.c_str());
|
|
534
|
+
obs_volmeter_t* volmeter = vol_it->second;
|
|
535
|
+
obs_volmeter_attach_source(volmeter, source);
|
|
536
|
+
|
|
537
|
+
// Flush the volmeter: send a zero signal in-case it never triggers any
|
|
538
|
+
// more callbacks. That can happen on selecting a device with no audio.
|
|
539
|
+
zeroVolmeter(name);
|
|
540
|
+
}
|
|
447
541
|
}
|
|
448
542
|
|
|
449
543
|
obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
|
|
@@ -451,7 +545,7 @@ obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
|
|
|
451
545
|
auto it = sources.find(name);
|
|
452
546
|
|
|
453
547
|
if (it == sources.end()) {
|
|
454
|
-
blog(LOG_WARNING, "Source %s not found", name.c_str());
|
|
548
|
+
blog(LOG_WARNING, "Source %s not found when getting properties", name.c_str());
|
|
455
549
|
throw std::runtime_error("Source not found!");
|
|
456
550
|
}
|
|
457
551
|
|
|
@@ -466,65 +560,30 @@ obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
|
|
|
466
560
|
return props;
|
|
467
561
|
}
|
|
468
562
|
|
|
469
|
-
void
|
|
470
|
-
Napi::Object obj = Napi::Object::New(env);
|
|
471
|
-
obj.Set("id", Napi::String::New(env, sd->id));
|
|
472
|
-
obj.Set("code", Napi::Number::New(env, sd->code));
|
|
473
|
-
cb.Call({ obj });
|
|
474
|
-
delete sd;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
void ObsInterface::output_signal_handler_starting(void *data, calldata_t *cd) {
|
|
478
|
-
long long code = calldata_int(cd, "code");
|
|
479
|
-
ObsInterface* self = static_cast<ObsInterface*>(data);
|
|
480
|
-
SignalData* sd = new SignalData{ "starting", code };
|
|
481
|
-
self->jscb.NonBlockingCall(sd, call_jscb);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
void ObsInterface::output_signal_handler_start(void *data, calldata_t *cd) {
|
|
485
|
-
long long code = calldata_int(cd, "code");
|
|
486
|
-
ObsInterface* self = static_cast<ObsInterface*>(data);
|
|
487
|
-
SignalData* sd = new SignalData{ "start", code };
|
|
488
|
-
self->jscb.NonBlockingCall(sd, call_jscb);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
void ObsInterface::output_signal_handler_stop(void *data, calldata_t *cd) {
|
|
563
|
+
void ObsInterface::output_signal_handler(void *data, calldata_t *cd) {
|
|
492
564
|
long long code = calldata_int(cd, "code");
|
|
493
|
-
ObsInterface* self = static_cast<ObsInterface*>(data);
|
|
494
|
-
SignalData* sd = new SignalData{ "stop", code };
|
|
495
|
-
self->jscb.NonBlockingCall(sd, call_jscb);
|
|
496
|
-
}
|
|
497
565
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
ObsInterface* self = static_cast<ObsInterface*>(data);
|
|
501
|
-
SignalData* sd = new SignalData{ "stopping", code };
|
|
502
|
-
self->jscb.NonBlockingCall(sd, call_jscb);
|
|
503
|
-
}
|
|
566
|
+
SignalContext* ctx = static_cast<SignalContext*>(data);
|
|
567
|
+
ObsInterface* self = ctx->self;
|
|
504
568
|
|
|
505
|
-
|
|
506
|
-
long long code = calldata_int(cd, "code");
|
|
507
|
-
ObsInterface* self = static_cast<ObsInterface*>(data);
|
|
508
|
-
SignalData* sd = new SignalData{ "saved", code };
|
|
569
|
+
SignalData* sd = new SignalData{ "output", ctx->id.c_str(), code };
|
|
509
570
|
self->jscb.NonBlockingCall(sd, call_jscb);
|
|
510
571
|
}
|
|
511
572
|
|
|
512
573
|
void ObsInterface::connect_signal_handlers(obs_output_t *output) {
|
|
513
574
|
signal_handler_t *sh = obs_output_get_signal_handler(output);
|
|
514
|
-
signal_handler_connect(sh, "
|
|
515
|
-
signal_handler_connect(sh, "
|
|
516
|
-
signal_handler_connect(sh, "stopping",
|
|
517
|
-
signal_handler_connect(sh, "stop",
|
|
518
|
-
signal_handler_connect(sh, "saved", output_signal_handler_saved, this);
|
|
575
|
+
signal_handler_connect(sh, "start", output_signal_handler, start_ctx);
|
|
576
|
+
signal_handler_connect(sh, "starting", output_signal_handler, starting_ctx);
|
|
577
|
+
signal_handler_connect(sh, "stopping", output_signal_handler, stopping_ctx);
|
|
578
|
+
signal_handler_connect(sh, "stop", output_signal_handler, stop_ctx);
|
|
519
579
|
}
|
|
520
580
|
|
|
521
581
|
void ObsInterface::disconnect_signal_handlers(obs_output_t *output) {
|
|
522
582
|
signal_handler_t *sh = obs_output_get_signal_handler(output);
|
|
523
|
-
signal_handler_disconnect(sh, "starting",
|
|
524
|
-
signal_handler_disconnect(sh, "start",
|
|
525
|
-
signal_handler_disconnect(sh, "stopping",
|
|
526
|
-
signal_handler_disconnect(sh, "stop",
|
|
527
|
-
signal_handler_disconnect(sh, "saved", output_signal_handler_saved, this);
|
|
583
|
+
signal_handler_disconnect(sh, "starting", output_signal_handler, starting_ctx);
|
|
584
|
+
signal_handler_disconnect(sh, "start", output_signal_handler, start_ctx);
|
|
585
|
+
signal_handler_disconnect(sh, "stopping", output_signal_handler, stopping_ctx);
|
|
586
|
+
signal_handler_disconnect(sh, "stop", output_signal_handler, stop_ctx);
|
|
528
587
|
}
|
|
529
588
|
|
|
530
589
|
bool draw_source_outline(obs_scene_t *scene, obs_sceneitem_t *item, void *p) {
|
|
@@ -582,6 +641,12 @@ bool draw_source_outline(obs_scene_t *scene, obs_sceneitem_t *item, void *p) {
|
|
|
582
641
|
gs_draw_sprite(nullptr, 0, 4.0f, height);
|
|
583
642
|
gs_matrix_pop();
|
|
584
643
|
|
|
644
|
+
// Dragging point box (25x25 pixels in bottom-right corner)
|
|
645
|
+
gs_matrix_push();
|
|
646
|
+
gs_matrix_translate3f(pos.x + width - 25.0f, pos.y + height - 25.0f, 0.0f);
|
|
647
|
+
gs_draw_sprite(nullptr, 0, 25.0f, 25.0f);
|
|
648
|
+
gs_matrix_pop();
|
|
649
|
+
|
|
585
650
|
gs_matrix_pop();
|
|
586
651
|
|
|
587
652
|
gs_technique_end_pass(tech);
|
|
@@ -622,15 +687,30 @@ void draw_callback(void* data, uint32_t cx, uint32_t cy) {
|
|
|
622
687
|
// Renders the scene now the graphics context is setup.
|
|
623
688
|
obs_render_main_texture();
|
|
624
689
|
|
|
625
|
-
// Draw boxes around sources.
|
|
626
|
-
obs_scene_t* scene = obs_get_scene_by_name("WCR Scene");
|
|
690
|
+
// Draw boxes around sources, if enabled.
|
|
627
691
|
if (obsInterface->getDrawSourceOutlineEnabled()) {
|
|
692
|
+
obs_scene_t* scene = obs_get_scene_by_name("Base Scene");
|
|
628
693
|
obs_scene_enum_items(scene, draw_source_outline, NULL);
|
|
694
|
+
obs_scene_release(scene);
|
|
629
695
|
}
|
|
630
|
-
obs_scene_release(scene);
|
|
631
696
|
|
|
632
697
|
gs_projection_pop();
|
|
633
698
|
gs_viewport_pop();
|
|
699
|
+
|
|
700
|
+
// Iterate over the sources and check for changes to size.
|
|
701
|
+
for (const auto& [name, source] : obsInterface->sources) {
|
|
702
|
+
SourceSize last = obsInterface->sizes[name];
|
|
703
|
+
|
|
704
|
+
uint32_t w = obs_source_get_width(source);
|
|
705
|
+
uint32_t h = obs_source_get_height(source);
|
|
706
|
+
|
|
707
|
+
if (w != last.width || h != last.height) {
|
|
708
|
+
blog(LOG_INFO, "Source %s changed size from (%d x %d) to (%d x %d)",
|
|
709
|
+
name.c_str(), last.width, last.height, w, h);
|
|
710
|
+
obsInterface->sourceCallback(name);
|
|
711
|
+
obsInterface->sizes[name] = { w, h };
|
|
712
|
+
}
|
|
713
|
+
}
|
|
634
714
|
}
|
|
635
715
|
|
|
636
716
|
void ObsInterface::initPreview(HWND parent) {
|
|
@@ -683,15 +763,15 @@ void ObsInterface::initPreview(HWND parent) {
|
|
|
683
763
|
obs_display_set_enabled(display, false);
|
|
684
764
|
}
|
|
685
765
|
|
|
686
|
-
void ObsInterface::
|
|
687
|
-
blog(LOG_INFO, "ObsInterface::
|
|
766
|
+
void ObsInterface::configurePreview(int x, int y, int width, int height) {
|
|
767
|
+
blog(LOG_INFO, "ObsInterface::configurePreview");
|
|
688
768
|
|
|
689
769
|
if (!preview_hwnd || !display) {
|
|
690
770
|
blog(LOG_ERROR, "Preview window not initialized");
|
|
691
771
|
return;
|
|
692
772
|
}
|
|
693
773
|
|
|
694
|
-
blog(LOG_INFO, "
|
|
774
|
+
blog(LOG_INFO, "Moving preview child window to (%d, %d) with size (%d x %d)", x, y, width, height);
|
|
695
775
|
|
|
696
776
|
// Resize and move the existing child window.
|
|
697
777
|
bool success = SetWindowPos(
|
|
@@ -710,8 +790,18 @@ void ObsInterface::showPreview(int x, int y, int width, int height) {
|
|
|
710
790
|
uint32_t w, h;
|
|
711
791
|
obs_display_size(display, &w, &h); // Get the display size to match the video context.
|
|
712
792
|
blog(LOG_INFO, "Current Display size set to (%d x %d)", w, h);
|
|
713
|
-
|
|
714
793
|
obs_display_resize(display, width, height);
|
|
794
|
+
obs_display_set_enabled(display, true);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
void ObsInterface::showPreview() {
|
|
798
|
+
blog(LOG_INFO, "ObsInterface::showPreview");
|
|
799
|
+
|
|
800
|
+
if (!preview_hwnd || !display) {
|
|
801
|
+
blog(LOG_ERROR, "Preview window not initialized");
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
|
|
715
805
|
ShowWindow(preview_hwnd, SW_SHOW);
|
|
716
806
|
obs_display_set_enabled(display, true);
|
|
717
807
|
}
|
|
@@ -723,14 +813,24 @@ void ObsInterface::hidePreview() {
|
|
|
723
813
|
ShowWindow(preview_hwnd, SW_HIDE);
|
|
724
814
|
blog(LOG_INFO, "Preview child window hidden");
|
|
725
815
|
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
void ObsInterface::disablePreview() {
|
|
819
|
+
blog(LOG_INFO, "ObsInterface::disablePreview");
|
|
820
|
+
|
|
821
|
+
if (!display) {
|
|
822
|
+
blog(LOG_ERROR, "Preview window not initialized");
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
726
825
|
|
|
826
|
+
hidePreview();
|
|
727
827
|
obs_display_set_enabled(display, false);
|
|
728
828
|
}
|
|
729
829
|
|
|
730
|
-
|
|
830
|
+
PreviewInfo ObsInterface::getPreviewInfo() {
|
|
731
831
|
if (!display) {
|
|
732
|
-
blog(LOG_WARNING, "Display not initialized");
|
|
733
|
-
return
|
|
832
|
+
blog(LOG_WARNING, "Display not initialized when calling getPreviewInfo");
|
|
833
|
+
return { 1920, 1080, 1920, 1080 }; // Default values
|
|
734
834
|
}
|
|
735
835
|
|
|
736
836
|
obs_video_info ovi;
|
|
@@ -739,19 +839,14 @@ float ObsInterface::getPreviewScaleFactor() {
|
|
|
739
839
|
uint32_t width, height;
|
|
740
840
|
obs_display_size(display, &width, &height);
|
|
741
841
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
if (scaleX < scaleY) {
|
|
749
|
-
previewScale = scaleX;
|
|
750
|
-
} else {
|
|
751
|
-
previewScale = scaleY;
|
|
752
|
-
}
|
|
842
|
+
PreviewInfo info = {
|
|
843
|
+
ovi.base_width,
|
|
844
|
+
ovi.base_height,
|
|
845
|
+
width,
|
|
846
|
+
height,
|
|
847
|
+
};
|
|
753
848
|
|
|
754
|
-
return
|
|
849
|
+
return info;
|
|
755
850
|
}
|
|
756
851
|
|
|
757
852
|
void ObsInterface::setDrawSourceOutline(bool enabled) {
|
|
@@ -765,7 +860,6 @@ bool ObsInterface::getDrawSourceOutlineEnabled() {
|
|
|
765
860
|
ObsInterface::ObsInterface(
|
|
766
861
|
const std::string& distPath,
|
|
767
862
|
const std::string& logPath,
|
|
768
|
-
const std::string& recordingPath,
|
|
769
863
|
Napi::ThreadSafeFunction cb
|
|
770
864
|
) {
|
|
771
865
|
// Setup logs first so we have logs for the initialization.
|
|
@@ -777,12 +871,16 @@ ObsInterface::ObsInterface(
|
|
|
777
871
|
|
|
778
872
|
// Setup callback function.
|
|
779
873
|
jscb = cb;
|
|
780
|
-
|
|
874
|
+
|
|
875
|
+
// Contexts for signal callbacks.
|
|
876
|
+
starting_ctx = new SignalContext{ this, "starting" };
|
|
877
|
+
start_ctx = new SignalContext{ this, "start" };
|
|
878
|
+
stopping_ctx = new SignalContext{ this, "stopping" };
|
|
879
|
+
stop_ctx = new SignalContext{ this, "stop" };
|
|
781
880
|
|
|
782
881
|
// Create the resources we rely on.
|
|
783
|
-
create_output();
|
|
784
882
|
create_scene();
|
|
785
|
-
|
|
883
|
+
create_output();
|
|
786
884
|
create_video_encoders();
|
|
787
885
|
create_audio_encoders();
|
|
788
886
|
}
|
|
@@ -790,16 +888,37 @@ ObsInterface::ObsInterface(
|
|
|
790
888
|
ObsInterface::~ObsInterface() {
|
|
791
889
|
blog(LOG_DEBUG, "Destroying ObsInterface");
|
|
792
890
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
891
|
+
for (auto& kv : volmeters) {
|
|
892
|
+
obs_volmeter_t* volmeter = kv.second;
|
|
893
|
+
obs_volmeter_remove_callback(volmeter, volmeter_callback, this);
|
|
894
|
+
obs_volmeter_detach_source(volmeter);
|
|
895
|
+
obs_volmeter_destroy(volmeter);
|
|
896
|
+
blog(LOG_INFO, "Volmeter deleted for source: %s", kv.first.c_str());
|
|
897
|
+
volmeters.erase(kv.first);
|
|
796
898
|
}
|
|
797
899
|
|
|
900
|
+
delete starting_ctx;
|
|
901
|
+
delete start_ctx;
|
|
902
|
+
delete stopping_ctx;
|
|
903
|
+
delete stop_ctx;
|
|
904
|
+
|
|
798
905
|
for (auto& kv : sources) {
|
|
799
906
|
std::string name = kv.first;
|
|
800
907
|
obs_source_t* source = kv.second;
|
|
908
|
+
|
|
909
|
+
auto filter_it = filters.find(name);
|
|
910
|
+
|
|
911
|
+
if (filter_it != filters.end()) {
|
|
912
|
+
obs_source_t* filter = filter_it->second;
|
|
913
|
+
obs_source_filter_remove(source, filter);
|
|
914
|
+
obs_source_release(filter);
|
|
915
|
+
filters.erase(name);
|
|
916
|
+
blog(LOG_INFO, "Filter removed for source: %s on shutdown", name.c_str());
|
|
917
|
+
}
|
|
918
|
+
|
|
801
919
|
blog(LOG_DEBUG, "Releasing source: %s", name.c_str());
|
|
802
920
|
obs_source_release(source);
|
|
921
|
+
sources.erase(name);
|
|
803
922
|
}
|
|
804
923
|
|
|
805
924
|
if (scene) {
|
|
@@ -807,24 +926,14 @@ ObsInterface::~ObsInterface() {
|
|
|
807
926
|
obs_scene_release(scene);
|
|
808
927
|
}
|
|
809
928
|
|
|
810
|
-
if (
|
|
811
|
-
if (obs_output_active(
|
|
812
|
-
blog(LOG_DEBUG, "Force stopping output");
|
|
813
|
-
obs_output_force_stop(buffer_output);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
blog(LOG_DEBUG, "Releasing output");
|
|
817
|
-
obs_output_release(buffer_output);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
if (file_output) {
|
|
821
|
-
if (obs_output_active(file_output)) {
|
|
929
|
+
if (output) {
|
|
930
|
+
if (obs_output_active(output)) {
|
|
822
931
|
blog(LOG_DEBUG, "Force stopping output");
|
|
823
|
-
obs_output_force_stop(
|
|
932
|
+
obs_output_force_stop(output);
|
|
824
933
|
}
|
|
825
934
|
|
|
826
935
|
blog(LOG_DEBUG, "Releasing output");
|
|
827
|
-
obs_output_release(
|
|
936
|
+
obs_output_release(output);
|
|
828
937
|
}
|
|
829
938
|
|
|
830
939
|
// if (video_encoder) {
|
|
@@ -839,18 +948,21 @@ ObsInterface::~ObsInterface() {
|
|
|
839
948
|
|
|
840
949
|
blog(LOG_DEBUG, "Now shutting down OBS");
|
|
841
950
|
obs_shutdown();
|
|
842
|
-
}
|
|
843
951
|
|
|
844
|
-
|
|
845
|
-
|
|
952
|
+
if (jscb) {
|
|
953
|
+
blog(LOG_DEBUG, "Releasing JavaScript callback");
|
|
954
|
+
jscb.Release();
|
|
955
|
+
}
|
|
956
|
+
}
|
|
846
957
|
|
|
958
|
+
void ObsInterface::setBuffering(bool value) {
|
|
847
959
|
if (obs_output_active(output)) {
|
|
848
960
|
blog(LOG_ERROR, "Cannot change buffering state while output is active");
|
|
849
|
-
|
|
961
|
+
throw new std::runtime_error("Cannot change buffering state while output is active");
|
|
850
962
|
}
|
|
851
963
|
|
|
852
964
|
buffering = value;
|
|
853
|
-
|
|
965
|
+
create_output();
|
|
854
966
|
}
|
|
855
967
|
|
|
856
968
|
void ObsInterface::startBuffering() {
|
|
@@ -861,8 +973,6 @@ void ObsInterface::startBuffering() {
|
|
|
861
973
|
throw std::runtime_error("Buffering is not enabled!");
|
|
862
974
|
}
|
|
863
975
|
|
|
864
|
-
obs_output_t* output = buffer_output;
|
|
865
|
-
|
|
866
976
|
if (!output) {
|
|
867
977
|
blog(LOG_ERROR, "Output is not initialized!");
|
|
868
978
|
throw std::runtime_error("Output is not initialized!");
|
|
@@ -887,7 +997,11 @@ void ObsInterface::startBuffering() {
|
|
|
887
997
|
|
|
888
998
|
void ObsInterface::startRecording(int offset) {
|
|
889
999
|
blog(LOG_INFO, "ObsInterface::startRecording enter");
|
|
890
|
-
|
|
1000
|
+
|
|
1001
|
+
if (recording_path == "") {
|
|
1002
|
+
blog(LOG_ERROR, "Recording path is not set");
|
|
1003
|
+
throw std::runtime_error("Recording path is not set");
|
|
1004
|
+
}
|
|
891
1005
|
|
|
892
1006
|
if (buffering) {
|
|
893
1007
|
bool is_active = obs_output_active(output);
|
|
@@ -906,9 +1020,17 @@ void ObsInterface::startRecording(int offset) {
|
|
|
906
1020
|
calldata_free(&cd);
|
|
907
1021
|
|
|
908
1022
|
if (!success) {
|
|
1023
|
+
blog(LOG_ERROR, "Failed to call convert procedure handler");
|
|
909
1024
|
throw std::runtime_error("Failed to call convert procedure handler");
|
|
910
1025
|
}
|
|
911
1026
|
} else {
|
|
1027
|
+
obs_data_t *ffmpeg_settings = obs_data_create();
|
|
1028
|
+
std::string filename = recording_path + "\\" + get_current_date_time() + ".mp4";
|
|
1029
|
+
obs_data_set_string(ffmpeg_settings, "path", filename.c_str());
|
|
1030
|
+
obs_output_update(output, ffmpeg_settings);
|
|
1031
|
+
obs_data_release(ffmpeg_settings);
|
|
1032
|
+
unbuffered_output_filename = filename;
|
|
1033
|
+
|
|
912
1034
|
blog(LOG_INFO, "Starting ffmpeg_muxer output");
|
|
913
1035
|
|
|
914
1036
|
bool is_active = obs_output_active(output);
|
|
@@ -933,7 +1055,6 @@ void ObsInterface::startRecording(int offset) {
|
|
|
933
1055
|
|
|
934
1056
|
void ObsInterface::stopRecording() {
|
|
935
1057
|
blog(LOG_INFO, "ObsInterface::stopRecording enter");
|
|
936
|
-
obs_output_t* output = buffering ? buffer_output : file_output;
|
|
937
1058
|
bool is_active = obs_output_active(output);
|
|
938
1059
|
|
|
939
1060
|
if (!is_active) {
|
|
@@ -945,19 +1066,31 @@ void ObsInterface::stopRecording() {
|
|
|
945
1066
|
blog(LOG_INFO, "ObsInterface::stopRecording exited");
|
|
946
1067
|
}
|
|
947
1068
|
|
|
1069
|
+
void ObsInterface::forceStopRecording() {
|
|
1070
|
+
blog(LOG_INFO, "ObsInterface::forceStopRecording enter");
|
|
1071
|
+
bool is_active = obs_output_active(output);
|
|
1072
|
+
|
|
1073
|
+
if (!is_active) {
|
|
1074
|
+
blog(LOG_WARNING, "Output is not active");
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
obs_output_force_stop(output);
|
|
1079
|
+
blog(LOG_INFO, "ObsInterface::forceStopRecording exited");
|
|
1080
|
+
}
|
|
1081
|
+
|
|
948
1082
|
std::string ObsInterface::getLastRecording() {
|
|
949
1083
|
blog(LOG_INFO, "calling get last replay proc handler");
|
|
950
1084
|
calldata cd;
|
|
951
1085
|
calldata_init(&cd);
|
|
952
1086
|
|
|
953
|
-
obs_output_t* output = buffering ? buffer_output : file_output;
|
|
954
1087
|
proc_handler_t *ph = obs_output_get_proc_handler(output);
|
|
955
1088
|
|
|
956
1089
|
const char* type = obs_output_get_id(output);
|
|
957
1090
|
|
|
958
1091
|
if (!buffering) {
|
|
959
1092
|
blog(LOG_INFO, "Getting last recording path from ffmpeg_muxer");
|
|
960
|
-
return
|
|
1093
|
+
return unbuffered_output_filename;
|
|
961
1094
|
}
|
|
962
1095
|
|
|
963
1096
|
bool success = proc_handler_call(ph, "get_last_replay", &cd);
|
|
@@ -984,7 +1117,7 @@ void ObsInterface::addSourceToScene(std::string name) {
|
|
|
984
1117
|
auto it = sources.find(name);
|
|
985
1118
|
|
|
986
1119
|
if (it == sources.end()) {
|
|
987
|
-
blog(LOG_WARNING, "Source %s not found", name.c_str());
|
|
1120
|
+
blog(LOG_WARNING, "Source %s not found when adding to scene", name.c_str());
|
|
988
1121
|
throw std::runtime_error("Source not found!");
|
|
989
1122
|
}
|
|
990
1123
|
|
|
@@ -1021,7 +1154,7 @@ void ObsInterface::getSourcePos(std::string name, vec2* pos, vec2* size, vec2* s
|
|
|
1021
1154
|
auto it = sources.find(name);
|
|
1022
1155
|
|
|
1023
1156
|
if (it == sources.end()) {
|
|
1024
|
-
blog(LOG_WARNING, "Source %s not found", name.c_str());
|
|
1157
|
+
blog(LOG_WARNING, "Source %s not found when getting source position", name.c_str());
|
|
1025
1158
|
throw std::runtime_error("Source not found!");
|
|
1026
1159
|
}
|
|
1027
1160
|
|
|
@@ -1057,4 +1190,181 @@ void ObsInterface::setSourcePos(std::string name, vec2* pos, vec2* scale) {
|
|
|
1057
1190
|
|
|
1058
1191
|
obs_sceneitem_set_pos(item, pos);
|
|
1059
1192
|
obs_sceneitem_set_scale(item, scale);
|
|
1060
|
-
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
std::vector<std::string> ObsInterface::listAvailableVideoEncoders()
|
|
1196
|
+
{
|
|
1197
|
+
std::vector<std::string> encoders;
|
|
1198
|
+
size_t idx = 0;
|
|
1199
|
+
const char *encoder_type;
|
|
1200
|
+
|
|
1201
|
+
while (obs_enum_encoder_types(idx++, &encoder_type)) {
|
|
1202
|
+
bool video = obs_get_encoder_type(encoder_type) == OBS_ENCODER_VIDEO;
|
|
1203
|
+
|
|
1204
|
+
if (video)
|
|
1205
|
+
encoders.emplace_back(encoder_type);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
return encoders;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
void ObsInterface::setVideoEncoder(std::string id, obs_data_t* settings) {
|
|
1212
|
+
if (obs_output_active(output)) {
|
|
1213
|
+
blog(LOG_WARNING, "Cannot change video encoder while output is active");
|
|
1214
|
+
throw new std::runtime_error("Output is active when trying to change encoder");
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
video_encoder_id = id;
|
|
1218
|
+
obs_data_release(video_encoder_settings);
|
|
1219
|
+
video_encoder_settings = settings;
|
|
1220
|
+
create_video_encoders();
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
void ObsInterface::setMuteAudioInputs(bool mute) {
|
|
1224
|
+
// Loop over all sources, and set the mute state if they are of type "wasapi_input_capture".
|
|
1225
|
+
for (const auto& kv : sources) {
|
|
1226
|
+
const std::string& name = kv.first;
|
|
1227
|
+
obs_source_t* source = kv.second;
|
|
1228
|
+
|
|
1229
|
+
if (!source) {
|
|
1230
|
+
blog(LOG_WARNING, "Source %s not found when muting audio inputs", name.c_str());
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
const char* type = obs_source_get_id(source);
|
|
1235
|
+
|
|
1236
|
+
if (strcmp(type, AUDIO_INPUT) == 0) {
|
|
1237
|
+
obs_source_set_muted(source, mute);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
void ObsInterface::setSourceVolume(std::string name, float volume) {
|
|
1243
|
+
blog(LOG_INFO, "Setting source %s volume to %f", name.c_str(), volume);
|
|
1244
|
+
|
|
1245
|
+
auto it = sources.find(name);
|
|
1246
|
+
|
|
1247
|
+
if (it == sources.end()) {
|
|
1248
|
+
blog(LOG_WARNING, "Source %s not found when setting volume", name.c_str());
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
obs_source_t* source = it->second;
|
|
1253
|
+
const char* type = obs_source_get_id(source);
|
|
1254
|
+
|
|
1255
|
+
bool audio =
|
|
1256
|
+
strcmp(type, AUDIO_OUTPUT) == 0 ||
|
|
1257
|
+
strcmp(type, AUDIO_INPUT) == 0 ||
|
|
1258
|
+
strcmp(type, AUDIO_PROCESS) == 0;
|
|
1259
|
+
|
|
1260
|
+
if (!audio) {
|
|
1261
|
+
blog(LOG_WARNING, "Source %s is not a valid audio source", name.c_str());
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
obs_source_set_volume(source, volume);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
void ObsInterface::setVolmeterEnabled(bool enabled) {
|
|
1269
|
+
blog(LOG_INFO, "Setting volmeter enabled: %d", enabled);
|
|
1270
|
+
volmeter_enabled = enabled;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
void ObsInterface::setForceMono(bool enabled) {
|
|
1274
|
+
blog(LOG_INFO, "%s force mono on all input sources", enabled ? "Enabling" : "Disabling");
|
|
1275
|
+
force_mono = enabled;
|
|
1276
|
+
|
|
1277
|
+
// Loop over existing sources and update the force mono flags.
|
|
1278
|
+
for (const auto& kv : sources) {
|
|
1279
|
+
const std::string& name = kv.first;
|
|
1280
|
+
obs_source_t* source = kv.second;
|
|
1281
|
+
|
|
1282
|
+
if (!source) {
|
|
1283
|
+
blog(LOG_WARNING, "Source %s not found when setting force mono", name.c_str());
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
const char* type = obs_source_get_id(source);
|
|
1288
|
+
|
|
1289
|
+
if (strcmp(type, AUDIO_INPUT) != 0) {
|
|
1290
|
+
// Force mono is only applicable to microphones, skip other types.
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
if (enabled) {
|
|
1295
|
+
blog(LOG_INFO, "Setting force mono flag on source %s", name.c_str());
|
|
1296
|
+
uint32_t flags = obs_source_get_flags(source);
|
|
1297
|
+
obs_source_set_flags(source, flags | OBS_SOURCE_FLAG_FORCE_MONO);
|
|
1298
|
+
} else {
|
|
1299
|
+
blog(LOG_INFO, "Unsetting force mono flag on source %s", name.c_str());
|
|
1300
|
+
uint32_t flags = obs_source_get_flags(source);
|
|
1301
|
+
obs_source_set_flags(source, flags & ~OBS_SOURCE_FLAG_FORCE_MONO);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
void ObsInterface::setAudioSuppression(bool enabled) {
|
|
1307
|
+
blog(LOG_INFO, "%s audio suppression on all input devices", enabled ? "Enabling" : "Disabling");
|
|
1308
|
+
audio_suppression = enabled;
|
|
1309
|
+
|
|
1310
|
+
// Loop over existing sources and add filters to any that need it.
|
|
1311
|
+
for (const auto& kv : sources) {
|
|
1312
|
+
const std::string& name = kv.first;
|
|
1313
|
+
obs_source_t* source = kv.second;
|
|
1314
|
+
|
|
1315
|
+
if (!source) {
|
|
1316
|
+
blog(LOG_WARNING, "Source %s not found when adding filters", name.c_str());
|
|
1317
|
+
continue;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
const char* type = obs_source_get_id(source);
|
|
1321
|
+
|
|
1322
|
+
if (strcmp(type, AUDIO_INPUT) != 0) {
|
|
1323
|
+
// Don't care about non-input sources. This is purely for suppressing
|
|
1324
|
+
// microphone background noise.
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// Check for a filter existing and add or remove it as appropriate.
|
|
1329
|
+
auto filter_it = filters.find(name);
|
|
1330
|
+
|
|
1331
|
+
if (audio_suppression && filter_it == filters.end()) {
|
|
1332
|
+
blog(LOG_INFO, "Setting up filter for source: %s", name.c_str());
|
|
1333
|
+
|
|
1334
|
+
std::string filter_name = "Filter for " + name;
|
|
1335
|
+
|
|
1336
|
+
obs_source_t *filter = obs_source_create(
|
|
1337
|
+
"noise_suppress_filter_v2",
|
|
1338
|
+
filter_name.c_str(),
|
|
1339
|
+
nullptr, // Defaults are sensible.
|
|
1340
|
+
nullptr
|
|
1341
|
+
);
|
|
1342
|
+
|
|
1343
|
+
if (!filter) {
|
|
1344
|
+
blog(LOG_ERROR, "Failed to create filter for source: %s", name.c_str());
|
|
1345
|
+
throw std::runtime_error("Failed to create filter!");
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
filters[name] = filter;
|
|
1349
|
+
obs_source_filter_add(source, filter);
|
|
1350
|
+
} else if (!audio_suppression && filter_it != filters.end()) {
|
|
1351
|
+
blog(LOG_INFO, "Removing filters for source: %s", name.c_str());
|
|
1352
|
+
obs_source_t* filter = filter_it->second;
|
|
1353
|
+
obs_source_filter_remove(source, filter);
|
|
1354
|
+
filters.erase(name);
|
|
1355
|
+
obs_source_release(filter);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
void ObsInterface::sourceCallback(std::string name) {
|
|
1361
|
+
blog(LOG_INFO, "Source callback triggered for %s", name.c_str());
|
|
1362
|
+
SignalData* sd = new SignalData{ "source", name.c_str(), 0 };
|
|
1363
|
+
jscb.NonBlockingCall(sd, call_jscb);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
void ObsInterface::zeroVolmeter(std::string name) {
|
|
1367
|
+
blog(LOG_INFO, "Zeroing volmeter for %s", name.c_str());
|
|
1368
|
+
SignalData* sd = new SignalData{ "volmeter", name.c_str(), 0, 0 };
|
|
1369
|
+
jscb.NonBlockingCall(sd, call_jscb);
|
|
1370
|
+
}
|