noobs 0.0.115 → 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.
Files changed (209) hide show
  1. package/dist/data/obs-plugins/obs-filters/LUTs/black_and_white.png +0 -0
  2. package/dist/data/obs-plugins/obs-filters/LUTs/grayscale.cube +32769 -0
  3. package/dist/data/obs-plugins/obs-filters/LUTs/grayscale.png +0 -0
  4. package/dist/data/obs-plugins/obs-filters/LUTs/invert.png +0 -0
  5. package/dist/data/obs-plugins/obs-filters/LUTs/original.cube +33 -0
  6. package/dist/data/obs-plugins/obs-filters/LUTs/original.png +0 -0
  7. package/dist/data/obs-plugins/obs-filters/LUTs/posterize.png +0 -0
  8. package/dist/data/obs-plugins/obs-filters/LUTs/red_isolated.png +0 -0
  9. package/dist/data/obs-plugins/obs-filters/LUTs/teal_lows_orange_highs.png +0 -0
  10. package/dist/data/obs-plugins/obs-filters/blend_add_filter.effect +54 -0
  11. package/dist/data/obs-plugins/obs-filters/blend_mul_filter.effect +54 -0
  12. package/dist/data/obs-plugins/obs-filters/blend_sub_filter.effect +54 -0
  13. package/dist/data/obs-plugins/obs-filters/chroma_key_filter.effect +99 -0
  14. package/dist/data/obs-plugins/obs-filters/chroma_key_filter_v2.effect +111 -0
  15. package/dist/data/obs-plugins/obs-filters/color.effect +95 -0
  16. package/dist/data/obs-plugins/obs-filters/color_correction_filter.effect +74 -0
  17. package/dist/data/obs-plugins/obs-filters/color_grade_filter.effect +177 -0
  18. package/dist/data/obs-plugins/obs-filters/color_key_filter.effect +65 -0
  19. package/dist/data/obs-plugins/obs-filters/color_key_filter_v2.effect +77 -0
  20. package/dist/data/obs-plugins/obs-filters/crop_filter.effect +95 -0
  21. package/dist/data/obs-plugins/obs-filters/hdr_tonemap_filter.effect +97 -0
  22. package/dist/data/obs-plugins/obs-filters/locale/af-ZA.ini +89 -0
  23. package/dist/data/obs-plugins/obs-filters/locale/ar-SA.ini +133 -0
  24. package/dist/data/obs-plugins/obs-filters/locale/az-AZ.ini +1 -0
  25. package/dist/data/obs-plugins/obs-filters/locale/ba-RU.ini +19 -0
  26. package/dist/data/obs-plugins/obs-filters/locale/be-BY.ini +134 -0
  27. package/dist/data/obs-plugins/obs-filters/locale/bg-BG.ini +71 -0
  28. package/dist/data/obs-plugins/obs-filters/locale/bn-BD.ini +106 -0
  29. package/dist/data/obs-plugins/obs-filters/locale/ca-ES.ini +128 -0
  30. package/dist/data/obs-plugins/obs-filters/locale/cs-CZ.ini +130 -0
  31. package/dist/data/obs-plugins/obs-filters/locale/da-DK.ini +119 -0
  32. package/dist/data/obs-plugins/obs-filters/locale/de-DE.ini +113 -0
  33. package/dist/data/obs-plugins/obs-filters/locale/el-GR.ini +128 -0
  34. package/dist/data/obs-plugins/obs-filters/locale/en-GB.ini +12 -0
  35. package/dist/data/obs-plugins/obs-filters/locale/en-US.ini +138 -0
  36. package/dist/data/obs-plugins/obs-filters/locale/eo-UY.ini +1 -0
  37. package/dist/data/obs-plugins/obs-filters/locale/es-ES.ini +129 -0
  38. package/dist/data/obs-plugins/obs-filters/locale/et-EE.ini +132 -0
  39. package/dist/data/obs-plugins/obs-filters/locale/eu-ES.ini +127 -0
  40. package/dist/data/obs-plugins/obs-filters/locale/fa-IR.ini +137 -0
  41. package/dist/data/obs-plugins/obs-filters/locale/fi-FI.ini +127 -0
  42. package/dist/data/obs-plugins/obs-filters/locale/fil-PH.ini +111 -0
  43. package/dist/data/obs-plugins/obs-filters/locale/fr-FR.ini +124 -0
  44. package/dist/data/obs-plugins/obs-filters/locale/gd-GB.ini +94 -0
  45. package/dist/data/obs-plugins/obs-filters/locale/gl-ES.ini +132 -0
  46. package/dist/data/obs-plugins/obs-filters/locale/he-IL.ini +135 -0
  47. package/dist/data/obs-plugins/obs-filters/locale/hi-IN.ini +134 -0
  48. package/dist/data/obs-plugins/obs-filters/locale/hr-HR.ini +80 -0
  49. package/dist/data/obs-plugins/obs-filters/locale/hu-HU.ini +131 -0
  50. package/dist/data/obs-plugins/obs-filters/locale/hy-AM.ini +130 -0
  51. package/dist/data/obs-plugins/obs-filters/locale/id-ID.ini +112 -0
  52. package/dist/data/obs-plugins/obs-filters/locale/it-IT.ini +130 -0
  53. package/dist/data/obs-plugins/obs-filters/locale/ja-JP.ini +134 -0
  54. package/dist/data/obs-plugins/obs-filters/locale/ka-GE.ini +133 -0
  55. package/dist/data/obs-plugins/obs-filters/locale/kaa.ini +41 -0
  56. package/dist/data/obs-plugins/obs-filters/locale/kab-KAB.ini +56 -0
  57. package/dist/data/obs-plugins/obs-filters/locale/kmr-TR.ini +126 -0
  58. package/dist/data/obs-plugins/obs-filters/locale/ko-KR.ini +134 -0
  59. package/dist/data/obs-plugins/obs-filters/locale/ms-MY.ini +131 -0
  60. package/dist/data/obs-plugins/obs-filters/locale/nb-NO.ini +115 -0
  61. package/dist/data/obs-plugins/obs-filters/locale/nl-NL.ini +111 -0
  62. package/dist/data/obs-plugins/obs-filters/locale/nn-NO.ini +19 -0
  63. package/dist/data/obs-plugins/obs-filters/locale/pl-PL.ini +127 -0
  64. package/dist/data/obs-plugins/obs-filters/locale/pt-BR.ini +128 -0
  65. package/dist/data/obs-plugins/obs-filters/locale/pt-PT.ini +130 -0
  66. package/dist/data/obs-plugins/obs-filters/locale/ro-RO.ini +125 -0
  67. package/dist/data/obs-plugins/obs-filters/locale/ru-RU.ini +135 -0
  68. package/dist/data/obs-plugins/obs-filters/locale/si-LK.ini +44 -0
  69. package/dist/data/obs-plugins/obs-filters/locale/sk-SK.ini +131 -0
  70. package/dist/data/obs-plugins/obs-filters/locale/sl-SI.ini +133 -0
  71. package/dist/data/obs-plugins/obs-filters/locale/sr-CS.ini +90 -0
  72. package/dist/data/obs-plugins/obs-filters/locale/sr-SP.ini +97 -0
  73. package/dist/data/obs-plugins/obs-filters/locale/sv-SE.ini +129 -0
  74. package/dist/data/obs-plugins/obs-filters/locale/szl-PL.ini +94 -0
  75. package/dist/data/obs-plugins/obs-filters/locale/ta-IN.ini +28 -0
  76. package/dist/data/obs-plugins/obs-filters/locale/th-TH.ini +132 -0
  77. package/dist/data/obs-plugins/obs-filters/locale/tl-PH.ini +64 -0
  78. package/dist/data/obs-plugins/obs-filters/locale/tr-TR.ini +129 -0
  79. package/dist/data/obs-plugins/obs-filters/locale/tt-RU.ini +27 -0
  80. package/dist/data/obs-plugins/obs-filters/locale/ug-CN.ini +133 -0
  81. package/dist/data/obs-plugins/obs-filters/locale/uk-UA.ini +135 -0
  82. package/dist/data/obs-plugins/obs-filters/locale/vi-VN.ini +131 -0
  83. package/dist/data/obs-plugins/obs-filters/locale/zh-CN.ini +138 -0
  84. package/dist/data/obs-plugins/obs-filters/locale/zh-TW.ini +136 -0
  85. package/dist/data/obs-plugins/obs-filters/luma_key_filter.effect +52 -0
  86. package/dist/data/obs-plugins/obs-filters/luma_key_filter_v2.effect +54 -0
  87. package/dist/data/obs-plugins/obs-filters/mask_alpha_filter.effect +54 -0
  88. package/dist/data/obs-plugins/obs-filters/mask_color_filter.effect +55 -0
  89. package/dist/data/obs-plugins/obs-filters/rtx_greenscreen.effect +183 -0
  90. package/dist/data/obs-plugins/obs-filters/sharpness.effect +76 -0
  91. package/dist/data/obs-plugins/obs-nvenc/locale/ar-SA.ini +60 -0
  92. package/dist/data/obs-plugins/obs-nvenc/locale/be-BY.ini +59 -0
  93. package/dist/data/obs-plugins/obs-nvenc/locale/bg-BG.ini +2 -0
  94. package/dist/data/obs-plugins/obs-nvenc/locale/ca-ES.ini +59 -0
  95. package/dist/data/obs-plugins/obs-nvenc/locale/cs-CZ.ini +55 -0
  96. package/dist/data/obs-plugins/obs-nvenc/locale/da-DK.ini +54 -0
  97. package/dist/data/obs-plugins/obs-nvenc/locale/de-DE.ini +55 -0
  98. package/dist/data/obs-plugins/obs-nvenc/locale/en-GB.ini +9 -0
  99. package/dist/data/obs-plugins/obs-nvenc/locale/en-US.ini +75 -0
  100. package/dist/data/obs-plugins/obs-nvenc/locale/es-ES.ini +59 -0
  101. package/dist/data/obs-plugins/obs-nvenc/locale/et-EE.ini +36 -0
  102. package/dist/data/obs-plugins/obs-nvenc/locale/fa-IR.ini +60 -0
  103. package/dist/data/obs-plugins/obs-nvenc/locale/fi-FI.ini +59 -0
  104. package/dist/data/obs-plugins/obs-nvenc/locale/fr-FR.ini +59 -0
  105. package/dist/data/obs-plugins/obs-nvenc/locale/gl-ES.ini +60 -0
  106. package/dist/data/obs-plugins/obs-nvenc/locale/he-IL.ini +60 -0
  107. package/dist/data/obs-plugins/obs-nvenc/locale/hi-IN.ini +60 -0
  108. package/dist/data/obs-plugins/obs-nvenc/locale/hu-HU.ini +60 -0
  109. package/dist/data/obs-plugins/obs-nvenc/locale/id-ID.ini +55 -0
  110. package/dist/data/obs-plugins/obs-nvenc/locale/is-IS.ini +2 -0
  111. package/dist/data/obs-plugins/obs-nvenc/locale/it-IT.ini +60 -0
  112. package/dist/data/obs-plugins/obs-nvenc/locale/ja-JP.ini +59 -0
  113. package/dist/data/obs-plugins/obs-nvenc/locale/ka-GE.ini +60 -0
  114. package/dist/data/obs-plugins/obs-nvenc/locale/kaa.ini +13 -0
  115. package/dist/data/obs-plugins/obs-nvenc/locale/kmr-TR.ini +20 -0
  116. package/dist/data/obs-plugins/obs-nvenc/locale/ko-KR.ini +58 -0
  117. package/dist/data/obs-plugins/obs-nvenc/locale/ms-MY.ini +58 -0
  118. package/dist/data/obs-plugins/obs-nvenc/locale/nb-NO.ini +24 -0
  119. package/dist/data/obs-plugins/obs-nvenc/locale/nl-NL.ini +54 -0
  120. package/dist/data/obs-plugins/obs-nvenc/locale/nn-NO.ini +1 -0
  121. package/dist/data/obs-plugins/obs-nvenc/locale/pl-PL.ini +55 -0
  122. package/dist/data/obs-plugins/obs-nvenc/locale/pt-BR.ini +58 -0
  123. package/dist/data/obs-plugins/obs-nvenc/locale/pt-PT.ini +60 -0
  124. package/dist/data/obs-plugins/obs-nvenc/locale/ru-RU.ini +60 -0
  125. package/dist/data/obs-plugins/obs-nvenc/locale/sk-SK.ini +56 -0
  126. package/dist/data/obs-plugins/obs-nvenc/locale/sv-SE.ini +58 -0
  127. package/dist/data/obs-plugins/obs-nvenc/locale/th-TH.ini +59 -0
  128. package/dist/data/obs-plugins/obs-nvenc/locale/tr-TR.ini +60 -0
  129. package/dist/data/obs-plugins/obs-nvenc/locale/tt-RU.ini +2 -0
  130. package/dist/data/obs-plugins/obs-nvenc/locale/ug-CN.ini +60 -0
  131. package/dist/data/obs-plugins/obs-nvenc/locale/uk-UA.ini +60 -0
  132. package/dist/data/obs-plugins/obs-nvenc/locale/vi-VN.ini +60 -0
  133. package/dist/data/obs-plugins/obs-nvenc/locale/zh-CN.ini +60 -0
  134. package/dist/data/obs-plugins/obs-nvenc/locale/zh-TW.ini +60 -0
  135. package/dist/data/obs-plugins/obs-qsv11/locale/af-ZA.ini +19 -0
  136. package/dist/data/obs-plugins/obs-qsv11/locale/ar-SA.ini +20 -0
  137. package/dist/data/obs-plugins/obs-qsv11/locale/ba-RU.ini +2 -0
  138. package/dist/data/obs-plugins/obs-qsv11/locale/be-BY.ini +20 -0
  139. package/dist/data/obs-plugins/obs-qsv11/locale/bg-BG.ini +7 -0
  140. package/dist/data/obs-plugins/obs-qsv11/locale/bn-BD.ini +8 -0
  141. package/dist/data/obs-plugins/obs-qsv11/locale/ca-ES.ini +20 -0
  142. package/dist/data/obs-plugins/obs-qsv11/locale/cs-CZ.ini +19 -0
  143. package/dist/data/obs-plugins/obs-qsv11/locale/da-DK.ini +19 -0
  144. package/dist/data/obs-plugins/obs-qsv11/locale/de-DE.ini +19 -0
  145. package/dist/data/obs-plugins/obs-qsv11/locale/el-GR.ini +20 -0
  146. package/dist/data/obs-plugins/obs-qsv11/locale/en-GB.ini +1 -0
  147. package/dist/data/obs-plugins/obs-qsv11/locale/en-US.ini +21 -0
  148. package/dist/data/obs-plugins/obs-qsv11/locale/es-ES.ini +19 -0
  149. package/dist/data/obs-plugins/obs-qsv11/locale/et-EE.ini +20 -0
  150. package/dist/data/obs-plugins/obs-qsv11/locale/eu-ES.ini +11 -0
  151. package/dist/data/obs-plugins/obs-qsv11/locale/fa-IR.ini +20 -0
  152. package/dist/data/obs-plugins/obs-qsv11/locale/fi-FI.ini +20 -0
  153. package/dist/data/obs-plugins/obs-qsv11/locale/fil-PH.ini +11 -0
  154. package/dist/data/obs-plugins/obs-qsv11/locale/fr-FR.ini +20 -0
  155. package/dist/data/obs-plugins/obs-qsv11/locale/gd-GB.ini +8 -0
  156. package/dist/data/obs-plugins/obs-qsv11/locale/gl-ES.ini +20 -0
  157. package/dist/data/obs-plugins/obs-qsv11/locale/he-IL.ini +20 -0
  158. package/dist/data/obs-plugins/obs-qsv11/locale/hi-IN.ini +20 -0
  159. package/dist/data/obs-plugins/obs-qsv11/locale/hr-HR.ini +14 -0
  160. package/dist/data/obs-plugins/obs-qsv11/locale/hu-HU.ini +20 -0
  161. package/dist/data/obs-plugins/obs-qsv11/locale/hy-AM.ini +13 -0
  162. package/dist/data/obs-plugins/obs-qsv11/locale/id-ID.ini +18 -0
  163. package/dist/data/obs-plugins/obs-qsv11/locale/is-IS.ini +1 -0
  164. package/dist/data/obs-plugins/obs-qsv11/locale/it-IT.ini +20 -0
  165. package/dist/data/obs-plugins/obs-qsv11/locale/ja-JP.ini +16 -0
  166. package/dist/data/obs-plugins/obs-qsv11/locale/ka-GE.ini +20 -0
  167. package/dist/data/obs-plugins/obs-qsv11/locale/kaa.ini +10 -0
  168. package/dist/data/obs-plugins/obs-qsv11/locale/kab-KAB.ini +5 -0
  169. package/dist/data/obs-plugins/obs-qsv11/locale/kmr-TR.ini +12 -0
  170. package/dist/data/obs-plugins/obs-qsv11/locale/ko-KR.ini +20 -0
  171. package/dist/data/obs-plugins/obs-qsv11/locale/ms-MY.ini +20 -0
  172. package/dist/data/obs-plugins/obs-qsv11/locale/nb-NO.ini +16 -0
  173. package/dist/data/obs-plugins/obs-qsv11/locale/nl-NL.ini +18 -0
  174. package/dist/data/obs-plugins/obs-qsv11/locale/nn-NO.ini +2 -0
  175. package/dist/data/obs-plugins/obs-qsv11/locale/oc-FR.ini +1 -0
  176. package/dist/data/obs-plugins/obs-qsv11/locale/pl-PL.ini +16 -0
  177. package/dist/data/obs-plugins/obs-qsv11/locale/pt-BR.ini +20 -0
  178. package/dist/data/obs-plugins/obs-qsv11/locale/pt-PT.ini +20 -0
  179. package/dist/data/obs-plugins/obs-qsv11/locale/ro-RO.ini +20 -0
  180. package/dist/data/obs-plugins/obs-qsv11/locale/ru-RU.ini +20 -0
  181. package/dist/data/obs-plugins/obs-qsv11/locale/si-LK.ini +8 -0
  182. package/dist/data/obs-plugins/obs-qsv11/locale/sk-SK.ini +19 -0
  183. package/dist/data/obs-plugins/obs-qsv11/locale/sl-SI.ini +20 -0
  184. package/dist/data/obs-plugins/obs-qsv11/locale/sr-CS.ini +6 -0
  185. package/dist/data/obs-plugins/obs-qsv11/locale/sr-SP.ini +10 -0
  186. package/dist/data/obs-plugins/obs-qsv11/locale/sv-SE.ini +20 -0
  187. package/dist/data/obs-plugins/obs-qsv11/locale/ta-IN.ini +2 -0
  188. package/dist/data/obs-plugins/obs-qsv11/locale/th-TH.ini +20 -0
  189. package/dist/data/obs-plugins/obs-qsv11/locale/tl-PH.ini +5 -0
  190. package/dist/data/obs-plugins/obs-qsv11/locale/tr-TR.ini +20 -0
  191. package/dist/data/obs-plugins/obs-qsv11/locale/tt-RU.ini +3 -0
  192. package/dist/data/obs-plugins/obs-qsv11/locale/ug-CN.ini +20 -0
  193. package/dist/data/obs-plugins/obs-qsv11/locale/uk-UA.ini +20 -0
  194. package/dist/data/obs-plugins/obs-qsv11/locale/vi-VN.ini +20 -0
  195. package/dist/data/obs-plugins/obs-qsv11/locale/zh-CN.ini +20 -0
  196. package/dist/data/obs-plugins/obs-qsv11/locale/zh-TW.ini +20 -0
  197. package/dist/noobs.node +0 -0
  198. package/dist/obs-plugins/image-source.dll +0 -0
  199. package/dist/obs-plugins/obs-filters.dll +0 -0
  200. package/dist/obs-plugins/obs-nvenc.dll +0 -0
  201. package/dist/obs-plugins/obs-qsv11.dll +0 -0
  202. package/dist/obs-plugins/obs-x264.dll +0 -0
  203. package/dist/obs-plugins/win-capture.dll +0 -0
  204. package/dist/obs-plugins/win-wasapi.dll +0 -0
  205. package/index.d.ts +5 -8
  206. package/package.json +1 -1
  207. package/src/main.cpp +34 -65
  208. package/src/obs_interface.cpp +295 -203
  209. package/src/obs_interface.h +28 -27
@@ -55,9 +55,10 @@ void ObsInterface::list_output_types()
55
55
  }
56
56
  }
57
57
 
58
- void ObsInterface::load_module(const char* module, const char* data) {
58
+ void ObsInterface::load_module(const char* module, const char* data, bool allowFail) {
59
59
  blog(LOG_INFO, "Loading module: %s", module);
60
60
  blog(LOG_INFO, "Data path: %s", data);
61
+ blog(LOG_INFO, "Allow fail: %d", allowFail);
61
62
 
62
63
  obs_module_t *ptr = NULL;
63
64
  int success = obs_open_module(&ptr, module, data);
@@ -69,8 +70,12 @@ void ObsInterface::load_module(const char* module, const char* data) {
69
70
 
70
71
  bool initmod = obs_init_module(ptr);
71
72
 
72
- if (!initmod) {
73
- blog(LOG_ERROR, "Failed to initialize module!");
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);
74
79
  throw std::runtime_error("Module initialization failed!");
75
80
  }
76
81
  }
@@ -189,17 +194,23 @@ void ObsInterface::init_obs(const std::string& distPath) {
189
194
  }
190
195
 
191
196
  std::vector<std::string> modules = {
192
- "obs-x264",
193
- "obs-ffmpeg",
197
+ "obs-x264", // Software encoder.
198
+ "obs-ffmpeg", // Contains AMF (AMD) encoder support.
194
199
  "win-capture", // Required for basically all forms of capture on Windows.
195
200
  "image-source", // Required for image sources.
196
- "win-wasapi" // Required for WASAPI audio input.
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.
197
205
  };
198
206
 
199
207
  for (const auto& module : modules) {
200
208
  std::string modulePath = pluginPath + module + ".dll";
201
209
  std::string moduleDataPath = pluginDataPath + module;
202
- load_module(modulePath.c_str(), moduleDataPath.c_str());
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);
203
214
  }
204
215
 
205
216
  obs_post_load_modules();
@@ -214,168 +225,119 @@ void ObsInterface::init_obs(const std::string& distPath) {
214
225
  void ObsInterface::create_output() {
215
226
  blog(LOG_INFO, "Create outputs");
216
227
 
217
- blog(LOG_INFO, "Creating replay buffer output");
218
- buffer_output = obs_output_create("replay_buffer", "Buffer Output", NULL, NULL);
228
+ const char* type = buffering ? "replay_buffer" : "ffmpeg_muxer";
229
+ const char* name = buffering ? "Buffer Output" : "File Output";
219
230
 
220
- if (!buffer_output) {
221
- blog(LOG_ERROR, "Failed to create buffer output!");
222
- throw std::runtime_error("Failed to create buffer output!");
231
+ if (output) {
232
+ blog(LOG_DEBUG, "Releasing existing output");
233
+ obs_output_release(output);
223
234
  }
224
235
 
225
- blog(LOG_INFO, "Creating file output");
226
- file_output = obs_output_create("ffmpeg_muxer", "File Output", NULL, NULL);
236
+ blog(LOG_INFO, "Creating replay buffer output");
237
+ output = obs_output_create(type, name, NULL, NULL);
227
238
 
228
- if (!file_output) {
229
- blog(LOG_ERROR, "Failed to create file output!");
230
- throw std::runtime_error("Failed to create file output!");
239
+ if (!output) {
240
+ blog(LOG_ERROR, "Failed to create output!");
241
+ throw std::runtime_error("Failed to create output!");
231
242
  }
232
243
 
233
- obs_data_t *buffer_settings = obs_data_create();
234
- blog(LOG_INFO, "Set replay_buffer settings");
235
- obs_data_set_int(buffer_settings, "max_time_sec", 60);
236
- obs_data_set_int(buffer_settings, "max_size_mb", 1024);
237
- obs_data_set_string(buffer_settings, "directory", recording_path.c_str());
238
- obs_data_set_string(buffer_settings, "format", "%CCYY-%MM-%DD %hh-%mm-%ss");
239
- obs_data_set_string(buffer_settings, "extension", "mp4");
240
- obs_output_update(buffer_output, buffer_settings);
241
- obs_data_release(buffer_settings);
244
+ obs_data_t *settings = obs_data_create();
242
245
 
243
- blog(LOG_INFO, "Set ffmpeg_muxer settings");
244
- obs_data_t *ffmpeg_settings = obs_data_create();
245
- // Need to specify the exact path for ffmpeg_muxer. We will write this again at start recording.
246
- std::string filename = recording_path + "\\" + get_current_date_time() + ".mp4";
247
- obs_data_set_string(ffmpeg_settings, "path", filename.c_str());
248
- unbuffered_output_filename = filename;
249
-
250
- // Apply and release the settings.
251
- obs_output_update(file_output, ffmpeg_settings);
252
- obs_data_release(ffmpeg_settings);
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
+ }
253
260
 
254
- // Add the signal handler callback.
255
- connect_signal_handlers(buffer_output);
256
- connect_signal_handlers(file_output);
261
+ obs_output_update(output, settings);
262
+ obs_data_release(settings);
263
+ connect_signal_handlers(output);
257
264
  }
258
265
 
259
266
  void ObsInterface::setRecordingDir(const std::string& recordingPath) {
260
- blog(LOG_INFO, "Set recording directory");
261
- // TODO make this work for file output also.
262
-
263
- obs_output_t *output = buffering ? buffer_output : file_output;
267
+ blog(LOG_INFO, "Set recording directory. Path: %s", recordingPath.c_str());
264
268
 
265
- if (!output) {
266
- blog(LOG_ERROR, "No output to update recording directory");
267
- throw std::runtime_error("Output not initialized");
268
- }
269
-
270
- // check its not active
271
269
  if (obs_output_active(output)) {
272
270
  blog(LOG_ERROR, "Output is active, cannot update recording path");
273
271
  throw std::runtime_error("Output is active, cannot update recording path");
274
272
  }
275
273
 
276
- obs_data_t *settings = obs_output_get_settings(output);
277
-
278
- if (!settings) {
279
- blog(LOG_ERROR, "Failed to get output settings");
280
- throw std::runtime_error("Failed to get output settings");
281
- }
274
+ recording_path = recordingPath;
275
+ create_output();
282
276
 
283
- obs_data_set_string(settings, "directory", recordingPath.c_str());
284
- obs_output_update(output, settings);
285
- obs_data_release(settings);
277
+ create_video_encoders();
278
+ create_audio_encoders();
286
279
  }
287
280
 
288
281
  void ObsInterface::create_video_encoders() {
289
282
  blog(LOG_INFO, "Set video encoder: %s", video_encoder_id.c_str());
290
283
 
291
- if (file_video_encoder) {
284
+ if (video_encoder) {
292
285
  blog(LOG_DEBUG, "Releasing file video encoder");
293
- obs_encoder_release(file_video_encoder);
294
- file_video_encoder = nullptr;
286
+ obs_encoder_release(video_encoder);
287
+ video_encoder = nullptr;
295
288
  }
296
289
 
297
- file_video_encoder = obs_video_encoder_create(
290
+ video_encoder = obs_video_encoder_create(
298
291
  video_encoder_id.c_str(),
299
292
  "noobs_file_encoder",
300
293
  video_encoder_settings,
301
294
  NULL
302
295
  );
303
296
 
304
- if (!file_video_encoder) {
297
+ if (!video_encoder) {
305
298
  blog(LOG_ERROR, "Failed to create video encoder!");
306
299
  throw std::runtime_error("Failed to create video encoder!");
307
300
  }
308
301
 
309
- if (buffer_video_encoder) {
310
- blog(LOG_DEBUG, "Releasing buffer video encoder");
311
- obs_encoder_release(buffer_video_encoder);
312
- buffer_video_encoder = nullptr;
313
- }
314
-
315
- buffer_video_encoder = obs_video_encoder_create(
316
- video_encoder_id.c_str(),
317
- "noobs_buffer_encoder",
318
- video_encoder_settings,
319
- NULL
320
- );
321
-
322
- if (!buffer_video_encoder) {
323
- blog(LOG_ERROR, "Failed to create buffer video encoder!");
324
- throw std::runtime_error("Failed to create buffer video encoder!");
325
- }
326
-
327
- obs_output_set_video_encoder(file_output, file_video_encoder);
328
- obs_output_set_video_encoder(buffer_output, buffer_video_encoder);
329
-
330
- obs_encoder_set_video(file_video_encoder, obs_get_video());
331
- 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());
332
304
  }
333
305
 
334
306
  void ObsInterface::create_audio_encoders() {
335
307
  blog(LOG_INFO, "Create audio encoder");
336
308
 
337
- // if (!output) {
338
- // blog(LOG_ERROR, "No output on create_audio_encoders");
339
- // throw std::runtime_error("Failed to create audio encoder!");
340
- // }
341
-
342
- // if (audio_encoder) {
343
- // blog(LOG_DEBUG, "Releasing audio encoder");
344
- // obs_encoder_release(audio_encoder);
345
- // audio_encoder = nullptr;
346
- // }
309
+ if (audio_encoder) {
310
+ blog(LOG_DEBUG, "Releasing audio encoder");
311
+ obs_encoder_release(audio_encoder);
312
+ audio_encoder = nullptr;
313
+ }
347
314
 
348
- file_audio_encoder = obs_audio_encoder_create("ffmpeg_aac", "aac_file", NULL, 0, NULL);
315
+ audio_encoder = obs_audio_encoder_create(
316
+ "ffmpeg_aac",
317
+ "aac_file",
318
+ NULL,
319
+ 0,
320
+ NULL
321
+ );
349
322
 
350
- if (!file_audio_encoder) {
323
+ if (!audio_encoder) {
351
324
  blog(LOG_ERROR, "Failed to create audio encoder!");
352
325
  throw std::runtime_error("Failed to create audio encoder!");
353
326
  }
354
327
 
355
- buffer_audio_encoder = obs_audio_encoder_create("ffmpeg_aac", "aac_buffer", NULL, 0, NULL);
356
-
357
- if (!buffer_audio_encoder) {
358
- blog(LOG_ERROR, "Failed to create buffer audio encoder!");
359
- throw std::runtime_error("Failed to create buffer audio encoder!");
360
- }
361
-
362
328
  blog(LOG_INFO, "Set audio encoder settings");
363
329
  obs_data_t *aenc_settings = obs_data_create();
364
330
  obs_data_set_int(aenc_settings, "bitrate", 128);
365
- obs_encoder_update(file_audio_encoder, aenc_settings);
366
- obs_encoder_update(buffer_audio_encoder, aenc_settings);
331
+ obs_encoder_update(audio_encoder, aenc_settings);
367
332
  obs_data_release(aenc_settings);
368
333
 
369
- obs_output_set_audio_encoder(file_output, file_audio_encoder, 0);
370
- obs_encoder_set_audio(file_audio_encoder, obs_get_audio());
371
-
372
- obs_output_set_audio_encoder(buffer_output, buffer_audio_encoder, 0);
373
- 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());
374
336
  }
375
337
 
376
338
  void ObsInterface::create_scene() {
377
339
  blog(LOG_INFO, "Create scene");
378
- scene = obs_scene_create("WCR Scene");
340
+ scene = obs_scene_create("Base Scene");
379
341
 
380
342
  if (!scene) {
381
343
  blog(LOG_ERROR, "Failed to create scene!");
@@ -414,7 +376,7 @@ void ObsInterface::volmeter_callback(void *data,
414
376
  self->jscb.NonBlockingCall(sd, call_jscb);
415
377
  }
416
378
 
417
- void ObsInterface::createSource(std::string name, std::string type) {
379
+ std::string ObsInterface::createSource(std::string name, std::string type) {
418
380
  blog(LOG_INFO, "Create source: %s of type %s", name.c_str(), type.c_str());
419
381
 
420
382
  obs_source_t *source = obs_source_create(
@@ -429,32 +391,59 @@ void ObsInterface::createSource(std::string name, std::string type) {
429
391
  throw std::runtime_error("Failed to create source!");
430
392
  }
431
393
 
432
- if (type == AUDIO_OUTPUT) {
433
- blog(LOG_INFO, "Setting output volume for source: %s to %d", name.c_str(), output_volume);
434
- obs_source_set_volume(source, output_volume);
435
- } else if (type == AUDIO_INPUT) {
436
- blog(LOG_INFO, "Setting input volume for source: %s to %d", name.c_str(), input_volume);
437
- obs_source_set_volume(source, input_volume);
438
- } else if (type == AUDIO_PROCESS) {
439
- blog(LOG_INFO, "Setting process volume for source: %s to %d", name.c_str(), process_volume);
440
- obs_source_set_volume(source, process_volume);
441
- }
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);
442
397
 
443
398
  if (type == AUDIO_OUTPUT || type == AUDIO_INPUT || type == AUDIO_PROCESS) {
444
- blog(LOG_INFO, "Creating volmeter for source: %s", name.c_str());
399
+ blog(LOG_INFO, "Creating volmeter for source: %s", real_name.c_str());
445
400
 
446
401
  obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_CUBIC);
447
402
  obs_volmeter_attach_source(volmeter, source);
448
403
 
449
- SignalContext* ctx = new SignalContext{ this, name }; // TODO don't leak this.
404
+ SignalContext* ctx = new SignalContext{ this, real_name }; // TODO don't leak this.
450
405
  obs_volmeter_add_callback(volmeter, volmeter_callback, ctx);
451
406
 
452
407
  // Store the volmeter in the volmeters map.
453
- volmeters[name] = volmeter;
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);
454
436
  }
455
437
 
456
438
  // Store the source in the sources map.
457
- sources[name] = source;
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;
458
447
  }
459
448
 
460
449
  void ObsInterface::deleteSource(std::string name) {
@@ -477,14 +466,27 @@ void ObsInterface::deleteSource(std::string name) {
477
466
  auto it = sources.find(name);
478
467
 
479
468
  if (it == sources.end()) {
480
- blog(LOG_WARNING, "Source %s not found", name.c_str());
469
+ blog(LOG_WARNING, "Source %s not found when deleting", name.c_str());
481
470
  return;
482
471
  }
483
472
 
484
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
+
485
486
  obs_source_remove(source); // ???
486
487
  obs_source_release(source);
487
488
  sources.erase(name);
489
+ sizes.erase(name);
488
490
  blog(LOG_INFO, "Source deleted: %s", name.c_str());
489
491
  }
490
492
 
@@ -494,7 +496,7 @@ obs_data_t* ObsInterface::getSourceSettings(std::string name) {
494
496
  auto it = sources.find(name);
495
497
 
496
498
  if (it == sources.end()) {
497
- blog(LOG_WARNING, "Source %s not found", name.c_str());
499
+ blog(LOG_WARNING, "Source %s not found when getting settings", name.c_str());
498
500
  throw std::runtime_error("Source not found!");
499
501
  }
500
502
 
@@ -515,13 +517,27 @@ void ObsInterface::setSourceSettings(std::string name, obs_data_t* settings) {
515
517
  auto it = sources.find(name);
516
518
 
517
519
  if (it == sources.end()) {
518
- blog(LOG_WARNING, "Source %s not found", name.c_str());
520
+ blog(LOG_WARNING, "Source %s not found when setting settings", name.c_str());
519
521
  throw std::runtime_error("Source not found!");
520
522
  }
521
523
 
522
524
  obs_source_t* source = it->second;
523
-
524
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
+ }
525
541
  }
526
542
 
527
543
  obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
@@ -529,7 +545,7 @@ obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
529
545
  auto it = sources.find(name);
530
546
 
531
547
  if (it == sources.end()) {
532
- blog(LOG_WARNING, "Source %s not found", name.c_str());
548
+ blog(LOG_WARNING, "Source %s not found when getting properties", name.c_str());
533
549
  throw std::runtime_error("Source not found!");
534
550
  }
535
551
 
@@ -671,15 +687,30 @@ void draw_callback(void* data, uint32_t cx, uint32_t cy) {
671
687
  // Renders the scene now the graphics context is setup.
672
688
  obs_render_main_texture();
673
689
 
674
- // Draw boxes around sources.
675
- obs_scene_t* scene = obs_get_scene_by_name("WCR Scene");
690
+ // Draw boxes around sources, if enabled.
676
691
  if (obsInterface->getDrawSourceOutlineEnabled()) {
692
+ obs_scene_t* scene = obs_get_scene_by_name("Base Scene");
677
693
  obs_scene_enum_items(scene, draw_source_outline, NULL);
694
+ obs_scene_release(scene);
678
695
  }
679
- obs_scene_release(scene);
680
696
 
681
697
  gs_projection_pop();
682
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
+ }
683
714
  }
684
715
 
685
716
  void ObsInterface::initPreview(HWND parent) {
@@ -733,7 +764,7 @@ void ObsInterface::initPreview(HWND parent) {
733
764
  }
734
765
 
735
766
  void ObsInterface::configurePreview(int x, int y, int width, int height) {
736
- blog(LOG_INFO, "ObsInterface::showPreview");
767
+ blog(LOG_INFO, "ObsInterface::configurePreview");
737
768
 
738
769
  if (!preview_hwnd || !display) {
739
770
  blog(LOG_ERROR, "Preview window not initialized");
@@ -829,7 +860,6 @@ bool ObsInterface::getDrawSourceOutlineEnabled() {
829
860
  ObsInterface::ObsInterface(
830
861
  const std::string& distPath,
831
862
  const std::string& logPath,
832
- const std::string& recordingPath,
833
863
  Napi::ThreadSafeFunction cb
834
864
  ) {
835
865
  // Setup logs first so we have logs for the initialization.
@@ -841,19 +871,16 @@ ObsInterface::ObsInterface(
841
871
 
842
872
  // Setup callback function.
843
873
  jscb = cb;
844
- recording_path = recordingPath;
845
874
 
875
+ // Contexts for signal callbacks.
846
876
  starting_ctx = new SignalContext{ this, "starting" };
847
877
  start_ctx = new SignalContext{ this, "start" };
848
878
  stopping_ctx = new SignalContext{ this, "stopping" };
849
879
  stop_ctx = new SignalContext{ this, "stop" };
850
880
 
851
881
  // Create the resources we rely on.
852
- create_output();
853
882
  create_scene();
854
-
855
- video_encoder_id = "obs_x264";
856
- video_encoder_settings = obs_data_create();
883
+ create_output();
857
884
  create_video_encoders();
858
885
  create_audio_encoders();
859
886
  }
@@ -878,6 +905,17 @@ ObsInterface::~ObsInterface() {
878
905
  for (auto& kv : sources) {
879
906
  std::string name = kv.first;
880
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
+
881
919
  blog(LOG_DEBUG, "Releasing source: %s", name.c_str());
882
920
  obs_source_release(source);
883
921
  sources.erase(name);
@@ -888,24 +926,14 @@ ObsInterface::~ObsInterface() {
888
926
  obs_scene_release(scene);
889
927
  }
890
928
 
891
- if (buffer_output) {
892
- if (obs_output_active(buffer_output)) {
929
+ if (output) {
930
+ if (obs_output_active(output)) {
893
931
  blog(LOG_DEBUG, "Force stopping output");
894
- obs_output_force_stop(buffer_output);
932
+ obs_output_force_stop(output);
895
933
  }
896
934
 
897
935
  blog(LOG_DEBUG, "Releasing output");
898
- obs_output_release(buffer_output);
899
- }
900
-
901
- if (file_output) {
902
- if (obs_output_active(file_output)) {
903
- blog(LOG_DEBUG, "Force stopping output");
904
- obs_output_force_stop(file_output);
905
- }
906
-
907
- blog(LOG_DEBUG, "Releasing output");
908
- obs_output_release(file_output);
936
+ obs_output_release(output);
909
937
  }
910
938
 
911
939
  // if (video_encoder) {
@@ -927,16 +955,14 @@ ObsInterface::~ObsInterface() {
927
955
  }
928
956
  }
929
957
 
930
- bool ObsInterface::setBuffering(bool value) {
931
- obs_output_t* output = buffering ? buffer_output : file_output;
932
-
958
+ void ObsInterface::setBuffering(bool value) {
933
959
  if (obs_output_active(output)) {
934
960
  blog(LOG_ERROR, "Cannot change buffering state while output is active");
935
- return false;
961
+ throw new std::runtime_error("Cannot change buffering state while output is active");
936
962
  }
937
963
 
938
964
  buffering = value;
939
- return buffering;
965
+ create_output();
940
966
  }
941
967
 
942
968
  void ObsInterface::startBuffering() {
@@ -947,8 +973,6 @@ void ObsInterface::startBuffering() {
947
973
  throw std::runtime_error("Buffering is not enabled!");
948
974
  }
949
975
 
950
- obs_output_t* output = buffer_output;
951
-
952
976
  if (!output) {
953
977
  blog(LOG_ERROR, "Output is not initialized!");
954
978
  throw std::runtime_error("Output is not initialized!");
@@ -973,7 +997,11 @@ void ObsInterface::startBuffering() {
973
997
 
974
998
  void ObsInterface::startRecording(int offset) {
975
999
  blog(LOG_INFO, "ObsInterface::startRecording enter");
976
- obs_output_t* output = buffering ? buffer_output : file_output;
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
+ }
977
1005
 
978
1006
  if (buffering) {
979
1007
  bool is_active = obs_output_active(output);
@@ -992,10 +1020,10 @@ void ObsInterface::startRecording(int offset) {
992
1020
  calldata_free(&cd);
993
1021
 
994
1022
  if (!success) {
1023
+ blog(LOG_ERROR, "Failed to call convert procedure handler");
995
1024
  throw std::runtime_error("Failed to call convert procedure handler");
996
1025
  }
997
1026
  } else {
998
-
999
1027
  obs_data_t *ffmpeg_settings = obs_data_create();
1000
1028
  std::string filename = recording_path + "\\" + get_current_date_time() + ".mp4";
1001
1029
  obs_data_set_string(ffmpeg_settings, "path", filename.c_str());
@@ -1027,7 +1055,6 @@ void ObsInterface::startRecording(int offset) {
1027
1055
 
1028
1056
  void ObsInterface::stopRecording() {
1029
1057
  blog(LOG_INFO, "ObsInterface::stopRecording enter");
1030
- obs_output_t* output = buffering ? buffer_output : file_output;
1031
1058
  bool is_active = obs_output_active(output);
1032
1059
 
1033
1060
  if (!is_active) {
@@ -1041,7 +1068,6 @@ void ObsInterface::stopRecording() {
1041
1068
 
1042
1069
  void ObsInterface::forceStopRecording() {
1043
1070
  blog(LOG_INFO, "ObsInterface::forceStopRecording enter");
1044
- obs_output_t* output = buffering ? buffer_output : file_output;
1045
1071
  bool is_active = obs_output_active(output);
1046
1072
 
1047
1073
  if (!is_active) {
@@ -1058,7 +1084,6 @@ std::string ObsInterface::getLastRecording() {
1058
1084
  calldata cd;
1059
1085
  calldata_init(&cd);
1060
1086
 
1061
- obs_output_t* output = buffering ? buffer_output : file_output;
1062
1087
  proc_handler_t *ph = obs_output_get_proc_handler(output);
1063
1088
 
1064
1089
  const char* type = obs_output_get_id(output);
@@ -1092,7 +1117,7 @@ void ObsInterface::addSourceToScene(std::string name) {
1092
1117
  auto it = sources.find(name);
1093
1118
 
1094
1119
  if (it == sources.end()) {
1095
- 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());
1096
1121
  throw std::runtime_error("Source not found!");
1097
1122
  }
1098
1123
 
@@ -1129,7 +1154,7 @@ void ObsInterface::getSourcePos(std::string name, vec2* pos, vec2* size, vec2* s
1129
1154
  auto it = sources.find(name);
1130
1155
 
1131
1156
  if (it == sources.end()) {
1132
- 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());
1133
1158
  throw std::runtime_error("Source not found!");
1134
1159
  }
1135
1160
 
@@ -1184,8 +1209,13 @@ std::vector<std::string> ObsInterface::listAvailableVideoEncoders()
1184
1209
  }
1185
1210
 
1186
1211
  void ObsInterface::setVideoEncoder(std::string id, obs_data_t* settings) {
1187
- // TODO don't allow this if output is active.
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
+
1188
1217
  video_encoder_id = id;
1218
+ obs_data_release(video_encoder_settings);
1189
1219
  video_encoder_settings = settings;
1190
1220
  create_video_encoders();
1191
1221
  }
@@ -1197,7 +1227,7 @@ void ObsInterface::setMuteAudioInputs(bool mute) {
1197
1227
  obs_source_t* source = kv.second;
1198
1228
 
1199
1229
  if (!source) {
1200
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1230
+ blog(LOG_WARNING, "Source %s not found when muting audio inputs", name.c_str());
1201
1231
  continue;
1202
1232
  }
1203
1233
 
@@ -1209,70 +1239,132 @@ void ObsInterface::setMuteAudioInputs(bool mute) {
1209
1239
  }
1210
1240
  }
1211
1241
 
1212
- void ObsInterface::setOutputVolume(float volume) {
1213
- blog(LOG_INFO, "Setting output volume to %f", volume);
1214
- output_volume = volume;
1242
+ void ObsInterface::setSourceVolume(std::string name, float volume) {
1243
+ blog(LOG_INFO, "Setting source %s volume to %f", name.c_str(), volume);
1215
1244
 
1216
- for (const auto& kv : sources) {
1217
- const std::string& name = kv.first;
1218
- obs_source_t* source = kv.second;
1245
+ auto it = sources.find(name);
1219
1246
 
1220
- if (!source) {
1221
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1222
- continue;
1223
- }
1247
+ if (it == sources.end()) {
1248
+ blog(LOG_WARNING, "Source %s not found when setting volume", name.c_str());
1249
+ return;
1250
+ }
1224
1251
 
1225
- const char* type = obs_source_get_id(source);
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;
1226
1259
 
1227
- if (strcmp(type, AUDIO_OUTPUT) == 0) {
1228
- obs_source_set_volume(source, output_volume);
1229
- }
1260
+ if (!audio) {
1261
+ blog(LOG_WARNING, "Source %s is not a valid audio source", name.c_str());
1262
+ return;
1230
1263
  }
1264
+
1265
+ obs_source_set_volume(source, volume);
1231
1266
  }
1232
1267
 
1233
- void ObsInterface::setInputVolume(float volume) {
1234
- blog(LOG_INFO, "Setting input volume to %f", volume);
1235
- input_volume = volume;
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;
1236
1276
 
1277
+ // Loop over existing sources and update the force mono flags.
1237
1278
  for (const auto& kv : sources) {
1238
1279
  const std::string& name = kv.first;
1239
1280
  obs_source_t* source = kv.second;
1240
1281
 
1241
1282
  if (!source) {
1242
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1283
+ blog(LOG_WARNING, "Source %s not found when setting force mono", name.c_str());
1243
1284
  continue;
1244
1285
  }
1245
1286
 
1246
1287
  const char* type = obs_source_get_id(source);
1247
1288
 
1248
- if (strcmp(type, AUDIO_INPUT) == 0) {
1249
- obs_source_set_volume(source, input_volume);
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);
1250
1302
  }
1251
1303
  }
1252
1304
  }
1253
1305
 
1254
- void ObsInterface::setProcessVolume(float volume) {
1255
- blog(LOG_INFO, "Setting process volume to %f", volume);
1256
- process_volume = volume;
1306
+ void ObsInterface::setAudioSuppression(bool enabled) {
1307
+ blog(LOG_INFO, "%s audio suppression on all input devices", enabled ? "Enabling" : "Disabling");
1308
+ audio_suppression = enabled;
1257
1309
 
1310
+ // Loop over existing sources and add filters to any that need it.
1258
1311
  for (const auto& kv : sources) {
1259
1312
  const std::string& name = kv.first;
1260
1313
  obs_source_t* source = kv.second;
1261
1314
 
1262
1315
  if (!source) {
1263
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1316
+ blog(LOG_WARNING, "Source %s not found when adding filters", name.c_str());
1264
1317
  continue;
1265
1318
  }
1266
1319
 
1267
1320
  const char* type = obs_source_get_id(source);
1268
1321
 
1269
- if (strcmp(type, AUDIO_PROCESS) == 0) {
1270
- obs_source_set_volume(source, process_volume);
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);
1271
1356
  }
1272
1357
  }
1273
1358
  }
1274
1359
 
1275
- void ObsInterface::setVolmeterEnabled(bool enabled) {
1276
- blog(LOG_INFO, "Setting volmeter enabled: %d", enabled);
1277
- volmeter_enabled = enabled;
1278
- }
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
+ }