noobs 0.0.115 → 0.0.131

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 +322 -215
  209. package/src/obs_interface.h +29 -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;
264
-
265
- if (!output) {
266
- blog(LOG_ERROR, "No output to update recording directory");
267
- throw std::runtime_error("Output not initialized");
268
- }
267
+ blog(LOG_INFO, "Set recording directory. Path: %s", recordingPath.c_str());
269
268
 
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,60 @@ 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 };
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
+ volmeter_cb_ctx[real_name] = ctx; // Track this so we can free it later.
410
+ }
411
+
412
+ if (type == AUDIO_INPUT && force_mono) {
413
+ blog(LOG_INFO, "Setting force mono for new source: %s", real_name.c_str());
414
+ uint32_t flags = obs_source_get_flags(source);
415
+ obs_source_set_flags(source, flags | OBS_SOURCE_FLAG_FORCE_MONO);
416
+ }
417
+
418
+ if (type == AUDIO_INPUT && audio_suppression) {
419
+ blog(LOG_INFO, "Setting up filter for new source: %s", real_name.c_str());
420
+ std::string filter_name = "Filter for " + real_name;
421
+
422
+
423
+ obs_source_t *filter = obs_source_create(
424
+ "noise_suppress_filter_v2",
425
+ filter_name.c_str(),
426
+ nullptr, // Defaults are sensible.
427
+ nullptr
428
+ );
429
+
430
+ if (!filter) {
431
+ blog(LOG_ERROR, "Failed to create filter for source: %s", real_name.c_str());
432
+ throw std::runtime_error("Failed to create filter!");
433
+ }
434
+
435
+ filters[real_name] = filter;
436
+ obs_source_filter_add(source, filter);
454
437
  }
455
438
 
456
439
  // Store the source in the sources map.
457
- sources[name] = source;
440
+ sources[real_name] = source;
441
+
442
+ // Store the dimensions so we can fire a callback if they change.
443
+ uint32_t w = obs_source_get_width(source);
444
+ uint32_t h = obs_source_get_height(source);
445
+ sizes[real_name] = { w, h };
446
+
447
+ return real_name;
458
448
  }
459
449
 
460
450
  void ObsInterface::deleteSource(std::string name) {
@@ -473,18 +463,40 @@ void ObsInterface::deleteSource(std::string name) {
473
463
  volmeters.erase(name);
474
464
  }
475
465
 
466
+ // Now deal with the callback context.
467
+ auto ctx_it = volmeter_cb_ctx.find(name);
468
+
469
+ if (ctx_it != volmeter_cb_ctx.end()) {
470
+ SignalContext* ctx = ctx_it->second;
471
+ delete ctx;
472
+ volmeter_cb_ctx.erase(ctx_it);
473
+ }
474
+
476
475
  // Now deal with the source itself.
477
476
  auto it = sources.find(name);
478
477
 
479
478
  if (it == sources.end()) {
480
- blog(LOG_WARNING, "Source %s not found", name.c_str());
479
+ blog(LOG_WARNING, "Source %s not found when deleting", name.c_str());
481
480
  return;
482
481
  }
483
482
 
484
483
  obs_source_t* source = it->second;
484
+
485
+ // Remove and release any filters.
486
+ auto filter_it = filters.find(name);
487
+
488
+ if (filter_it != filters.end()) {
489
+ obs_source_t* filter = filter_it->second;
490
+ obs_source_filter_remove(source, filter);
491
+ obs_source_release(filter);
492
+ filters.erase(name);
493
+ blog(LOG_INFO, "Filter deleted for source: %s", name.c_str());
494
+ }
495
+
485
496
  obs_source_remove(source); // ???
486
497
  obs_source_release(source);
487
498
  sources.erase(name);
499
+ sizes.erase(name);
488
500
  blog(LOG_INFO, "Source deleted: %s", name.c_str());
489
501
  }
490
502
 
@@ -494,7 +506,7 @@ obs_data_t* ObsInterface::getSourceSettings(std::string name) {
494
506
  auto it = sources.find(name);
495
507
 
496
508
  if (it == sources.end()) {
497
- blog(LOG_WARNING, "Source %s not found", name.c_str());
509
+ blog(LOG_WARNING, "Source %s not found when getting settings", name.c_str());
498
510
  throw std::runtime_error("Source not found!");
499
511
  }
500
512
 
@@ -506,7 +518,6 @@ obs_data_t* ObsInterface::getSourceSettings(std::string name) {
506
518
  throw std::runtime_error("Failed to get source settings!");
507
519
  }
508
520
 
509
- // obs_data_release(settings); TODO release after returning to client.
510
521
  return settings;
511
522
  }
512
523
 
@@ -515,13 +526,27 @@ void ObsInterface::setSourceSettings(std::string name, obs_data_t* settings) {
515
526
  auto it = sources.find(name);
516
527
 
517
528
  if (it == sources.end()) {
518
- blog(LOG_WARNING, "Source %s not found", name.c_str());
529
+ blog(LOG_WARNING, "Source %s not found when setting settings", name.c_str());
519
530
  throw std::runtime_error("Source not found!");
520
531
  }
521
532
 
522
533
  obs_source_t* source = it->second;
523
-
524
534
  obs_source_update(source, settings);
535
+
536
+ // If this is an audio source, it may have an attached volmeter.
537
+ auto vol_it = volmeters.find(name);
538
+
539
+ if (vol_it != volmeters.end()) {
540
+ // Rebind it. This avoids leaving it attached to stale audio stream
541
+ // in the event of a device change.
542
+ blog(LOG_INFO, "Rebinding volmeter for source: %s", name.c_str());
543
+ obs_volmeter_t* volmeter = vol_it->second;
544
+ obs_volmeter_attach_source(volmeter, source);
545
+
546
+ // Flush the volmeter: send a zero signal in-case it never triggers any
547
+ // more callbacks. That can happen on selecting a device with no audio.
548
+ zeroVolmeter(name);
549
+ }
525
550
  }
526
551
 
527
552
  obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
@@ -529,7 +554,7 @@ obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
529
554
  auto it = sources.find(name);
530
555
 
531
556
  if (it == sources.end()) {
532
- blog(LOG_WARNING, "Source %s not found", name.c_str());
557
+ blog(LOG_WARNING, "Source %s not found when getting properties", name.c_str());
533
558
  throw std::runtime_error("Source not found!");
534
559
  }
535
560
 
@@ -671,15 +696,30 @@ void draw_callback(void* data, uint32_t cx, uint32_t cy) {
671
696
  // Renders the scene now the graphics context is setup.
672
697
  obs_render_main_texture();
673
698
 
674
- // Draw boxes around sources.
675
- obs_scene_t* scene = obs_get_scene_by_name("WCR Scene");
699
+ // Draw boxes around sources, if enabled.
676
700
  if (obsInterface->getDrawSourceOutlineEnabled()) {
701
+ obs_scene_t* scene = obs_get_scene_by_name("Base Scene");
677
702
  obs_scene_enum_items(scene, draw_source_outline, NULL);
703
+ obs_scene_release(scene);
678
704
  }
679
- obs_scene_release(scene);
680
705
 
681
706
  gs_projection_pop();
682
707
  gs_viewport_pop();
708
+
709
+ // Iterate over the sources and check for changes to size.
710
+ for (const auto& [name, source] : obsInterface->sources) {
711
+ SourceSize last = obsInterface->sizes[name];
712
+
713
+ uint32_t w = obs_source_get_width(source);
714
+ uint32_t h = obs_source_get_height(source);
715
+
716
+ if (w != last.width || h != last.height) {
717
+ blog(LOG_INFO, "Source %s changed size from (%d x %d) to (%d x %d)",
718
+ name.c_str(), last.width, last.height, w, h);
719
+ obsInterface->sourceCallback(name);
720
+ obsInterface->sizes[name] = { w, h };
721
+ }
722
+ }
683
723
  }
684
724
 
685
725
  void ObsInterface::initPreview(HWND parent) {
@@ -712,8 +752,8 @@ void ObsInterface::initPreview(HWND parent) {
712
752
 
713
753
  gs_init_data gs_data = {};
714
754
  gs_data.adapter = 0;
715
- gs_data.cx = 1920; // TODO get from video context?
716
- gs_data.cy = 1080; // TODO get from video context?
755
+ gs_data.cx = 1920; // Gets overwritten when we call configurePreview().
756
+ gs_data.cy = 1080; // Gets overwritten when we call configurePreview().
717
757
  gs_data.format = GS_BGRA;
718
758
  gs_data.zsformat = GS_ZS_NONE;
719
759
  gs_data.num_backbuffers = 1;
@@ -733,7 +773,7 @@ void ObsInterface::initPreview(HWND parent) {
733
773
  }
734
774
 
735
775
  void ObsInterface::configurePreview(int x, int y, int width, int height) {
736
- blog(LOG_INFO, "ObsInterface::showPreview");
776
+ blog(LOG_INFO, "ObsInterface::configurePreview");
737
777
 
738
778
  if (!preview_hwnd || !display) {
739
779
  blog(LOG_ERROR, "Preview window not initialized");
@@ -756,9 +796,6 @@ void ObsInterface::configurePreview(int x, int y, int width, int height) {
756
796
  return;
757
797
  }
758
798
 
759
- uint32_t w, h;
760
- obs_display_size(display, &w, &h); // Get the display size to match the video context.
761
- blog(LOG_INFO, "Current Display size set to (%d x %d)", w, h);
762
799
  obs_display_resize(display, width, height);
763
800
  obs_display_set_enabled(display, true);
764
801
  }
@@ -829,7 +866,6 @@ bool ObsInterface::getDrawSourceOutlineEnabled() {
829
866
  ObsInterface::ObsInterface(
830
867
  const std::string& distPath,
831
868
  const std::string& logPath,
832
- const std::string& recordingPath,
833
869
  Napi::ThreadSafeFunction cb
834
870
  ) {
835
871
  // Setup logs first so we have logs for the initialization.
@@ -841,19 +877,16 @@ ObsInterface::ObsInterface(
841
877
 
842
878
  // Setup callback function.
843
879
  jscb = cb;
844
- recording_path = recordingPath;
845
880
 
881
+ // Contexts for signal callbacks.
846
882
  starting_ctx = new SignalContext{ this, "starting" };
847
883
  start_ctx = new SignalContext{ this, "start" };
848
884
  stopping_ctx = new SignalContext{ this, "stopping" };
849
885
  stop_ctx = new SignalContext{ this, "stop" };
850
886
 
851
887
  // Create the resources we rely on.
852
- create_output();
853
888
  create_scene();
854
-
855
- video_encoder_id = "obs_x264";
856
- video_encoder_settings = obs_data_create();
889
+ create_output();
857
890
  create_video_encoders();
858
891
  create_audio_encoders();
859
892
  }
@@ -870,6 +903,12 @@ ObsInterface::~ObsInterface() {
870
903
  volmeters.erase(kv.first);
871
904
  }
872
905
 
906
+ for (auto& kv : volmeter_cb_ctx) {
907
+ SignalContext* ctx = kv.second;
908
+ delete ctx;
909
+ volmeter_cb_ctx.erase(kv.first);
910
+ }
911
+
873
912
  delete starting_ctx;
874
913
  delete start_ctx;
875
914
  delete stopping_ctx;
@@ -878,6 +917,17 @@ ObsInterface::~ObsInterface() {
878
917
  for (auto& kv : sources) {
879
918
  std::string name = kv.first;
880
919
  obs_source_t* source = kv.second;
920
+
921
+ auto filter_it = filters.find(name);
922
+
923
+ if (filter_it != filters.end()) {
924
+ obs_source_t* filter = filter_it->second;
925
+ obs_source_filter_remove(source, filter);
926
+ obs_source_release(filter);
927
+ filters.erase(name);
928
+ blog(LOG_INFO, "Filter removed for source: %s on shutdown", name.c_str());
929
+ }
930
+
881
931
  blog(LOG_DEBUG, "Releasing source: %s", name.c_str());
882
932
  obs_source_release(source);
883
933
  sources.erase(name);
@@ -888,24 +938,14 @@ ObsInterface::~ObsInterface() {
888
938
  obs_scene_release(scene);
889
939
  }
890
940
 
891
- if (buffer_output) {
892
- if (obs_output_active(buffer_output)) {
941
+ if (output) {
942
+ if (obs_output_active(output)) {
893
943
  blog(LOG_DEBUG, "Force stopping output");
894
- obs_output_force_stop(buffer_output);
944
+ obs_output_force_stop(output);
895
945
  }
896
946
 
897
947
  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);
948
+ obs_output_release(output);
909
949
  }
910
950
 
911
951
  // if (video_encoder) {
@@ -927,16 +967,14 @@ ObsInterface::~ObsInterface() {
927
967
  }
928
968
  }
929
969
 
930
- bool ObsInterface::setBuffering(bool value) {
931
- obs_output_t* output = buffering ? buffer_output : file_output;
932
-
970
+ void ObsInterface::setBuffering(bool value) {
933
971
  if (obs_output_active(output)) {
934
972
  blog(LOG_ERROR, "Cannot change buffering state while output is active");
935
- return false;
973
+ throw new std::runtime_error("Cannot change buffering state while output is active");
936
974
  }
937
975
 
938
976
  buffering = value;
939
- return buffering;
977
+ create_output();
940
978
  }
941
979
 
942
980
  void ObsInterface::startBuffering() {
@@ -947,8 +985,6 @@ void ObsInterface::startBuffering() {
947
985
  throw std::runtime_error("Buffering is not enabled!");
948
986
  }
949
987
 
950
- obs_output_t* output = buffer_output;
951
-
952
988
  if (!output) {
953
989
  blog(LOG_ERROR, "Output is not initialized!");
954
990
  throw std::runtime_error("Output is not initialized!");
@@ -973,7 +1009,11 @@ void ObsInterface::startBuffering() {
973
1009
 
974
1010
  void ObsInterface::startRecording(int offset) {
975
1011
  blog(LOG_INFO, "ObsInterface::startRecording enter");
976
- obs_output_t* output = buffering ? buffer_output : file_output;
1012
+
1013
+ if (recording_path == "") {
1014
+ blog(LOG_ERROR, "Recording path is not set");
1015
+ throw std::runtime_error("Recording path is not set");
1016
+ }
977
1017
 
978
1018
  if (buffering) {
979
1019
  bool is_active = obs_output_active(output);
@@ -992,10 +1032,10 @@ void ObsInterface::startRecording(int offset) {
992
1032
  calldata_free(&cd);
993
1033
 
994
1034
  if (!success) {
1035
+ blog(LOG_ERROR, "Failed to call convert procedure handler");
995
1036
  throw std::runtime_error("Failed to call convert procedure handler");
996
1037
  }
997
1038
  } else {
998
-
999
1039
  obs_data_t *ffmpeg_settings = obs_data_create();
1000
1040
  std::string filename = recording_path + "\\" + get_current_date_time() + ".mp4";
1001
1041
  obs_data_set_string(ffmpeg_settings, "path", filename.c_str());
@@ -1027,7 +1067,6 @@ void ObsInterface::startRecording(int offset) {
1027
1067
 
1028
1068
  void ObsInterface::stopRecording() {
1029
1069
  blog(LOG_INFO, "ObsInterface::stopRecording enter");
1030
- obs_output_t* output = buffering ? buffer_output : file_output;
1031
1070
  bool is_active = obs_output_active(output);
1032
1071
 
1033
1072
  if (!is_active) {
@@ -1041,7 +1080,6 @@ void ObsInterface::stopRecording() {
1041
1080
 
1042
1081
  void ObsInterface::forceStopRecording() {
1043
1082
  blog(LOG_INFO, "ObsInterface::forceStopRecording enter");
1044
- obs_output_t* output = buffering ? buffer_output : file_output;
1045
1083
  bool is_active = obs_output_active(output);
1046
1084
 
1047
1085
  if (!is_active) {
@@ -1058,7 +1096,6 @@ std::string ObsInterface::getLastRecording() {
1058
1096
  calldata cd;
1059
1097
  calldata_init(&cd);
1060
1098
 
1061
- obs_output_t* output = buffering ? buffer_output : file_output;
1062
1099
  proc_handler_t *ph = obs_output_get_proc_handler(output);
1063
1100
 
1064
1101
  const char* type = obs_output_get_id(output);
@@ -1089,22 +1126,25 @@ std::string ObsInterface::getLastRecording() {
1089
1126
  void ObsInterface::addSourceToScene(std::string name) {
1090
1127
  blog(LOG_INFO, "ObsInterface::addSourceToScene called for source: %s", name.c_str());
1091
1128
 
1129
+ obs_sceneitem_t *item = obs_scene_find_source(scene, name.c_str());
1130
+
1131
+ if (item) {
1132
+ blog(LOG_WARNING, "Source %s already in scene", name.c_str());
1133
+ return;
1134
+ }
1135
+
1092
1136
  auto it = sources.find(name);
1093
1137
 
1094
1138
  if (it == sources.end()) {
1095
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1096
- throw std::runtime_error("Source not found!");
1139
+ blog(LOG_WARNING, "Source %s not found when adding to scene", name.c_str());
1140
+ return;
1097
1141
  }
1098
1142
 
1099
1143
  obs_source_t* source = it->second;
1100
-
1101
- // TODO refuse to add twice?
1102
- obs_sceneitem_t *item = obs_scene_add(scene, source);
1144
+ item = obs_scene_add(scene, source);
1103
1145
 
1104
1146
  if (!item) {
1105
1147
  blog(LOG_ERROR, "Failed to add source to scene: %s", name.c_str());
1106
- obs_source_release(source);
1107
- throw std::runtime_error("Failed to add source to scene");
1108
1148
  }
1109
1149
 
1110
1150
  blog(LOG_INFO, "ObsInterface::addSourceToScene exited");
@@ -1129,7 +1169,7 @@ void ObsInterface::getSourcePos(std::string name, vec2* pos, vec2* size, vec2* s
1129
1169
  auto it = sources.find(name);
1130
1170
 
1131
1171
  if (it == sources.end()) {
1132
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1172
+ blog(LOG_WARNING, "Source %s not found when getting source position", name.c_str());
1133
1173
  throw std::runtime_error("Source not found!");
1134
1174
  }
1135
1175
 
@@ -1184,8 +1224,13 @@ std::vector<std::string> ObsInterface::listAvailableVideoEncoders()
1184
1224
  }
1185
1225
 
1186
1226
  void ObsInterface::setVideoEncoder(std::string id, obs_data_t* settings) {
1187
- // TODO don't allow this if output is active.
1227
+ if (obs_output_active(output)) {
1228
+ blog(LOG_WARNING, "Cannot change video encoder while output is active");
1229
+ throw new std::runtime_error("Output is active when trying to change encoder");
1230
+ }
1231
+
1188
1232
  video_encoder_id = id;
1233
+ obs_data_release(video_encoder_settings);
1189
1234
  video_encoder_settings = settings;
1190
1235
  create_video_encoders();
1191
1236
  }
@@ -1197,7 +1242,7 @@ void ObsInterface::setMuteAudioInputs(bool mute) {
1197
1242
  obs_source_t* source = kv.second;
1198
1243
 
1199
1244
  if (!source) {
1200
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1245
+ blog(LOG_WARNING, "Source %s not found when muting audio inputs", name.c_str());
1201
1246
  continue;
1202
1247
  }
1203
1248
 
@@ -1209,70 +1254,132 @@ void ObsInterface::setMuteAudioInputs(bool mute) {
1209
1254
  }
1210
1255
  }
1211
1256
 
1212
- void ObsInterface::setOutputVolume(float volume) {
1213
- blog(LOG_INFO, "Setting output volume to %f", volume);
1214
- output_volume = volume;
1257
+ void ObsInterface::setSourceVolume(std::string name, float volume) {
1258
+ blog(LOG_INFO, "Setting source %s volume to %f", name.c_str(), volume);
1215
1259
 
1216
- for (const auto& kv : sources) {
1217
- const std::string& name = kv.first;
1218
- obs_source_t* source = kv.second;
1260
+ auto it = sources.find(name);
1219
1261
 
1220
- if (!source) {
1221
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1222
- continue;
1223
- }
1262
+ if (it == sources.end()) {
1263
+ blog(LOG_WARNING, "Source %s not found when setting volume", name.c_str());
1264
+ return;
1265
+ }
1224
1266
 
1225
- const char* type = obs_source_get_id(source);
1267
+ obs_source_t* source = it->second;
1268
+ const char* type = obs_source_get_id(source);
1269
+
1270
+ bool audio =
1271
+ strcmp(type, AUDIO_OUTPUT) == 0 ||
1272
+ strcmp(type, AUDIO_INPUT) == 0 ||
1273
+ strcmp(type, AUDIO_PROCESS) == 0;
1226
1274
 
1227
- if (strcmp(type, AUDIO_OUTPUT) == 0) {
1228
- obs_source_set_volume(source, output_volume);
1229
- }
1275
+ if (!audio) {
1276
+ blog(LOG_WARNING, "Source %s is not a valid audio source", name.c_str());
1277
+ return;
1230
1278
  }
1279
+
1280
+ obs_source_set_volume(source, volume);
1281
+ }
1282
+
1283
+ void ObsInterface::setVolmeterEnabled(bool enabled) {
1284
+ blog(LOG_INFO, "Setting volmeter enabled: %d", enabled);
1285
+ volmeter_enabled = enabled;
1231
1286
  }
1232
1287
 
1233
- void ObsInterface::setInputVolume(float volume) {
1234
- blog(LOG_INFO, "Setting input volume to %f", volume);
1235
- input_volume = volume;
1288
+ void ObsInterface::setForceMono(bool enabled) {
1289
+ blog(LOG_INFO, "%s force mono on all input sources", enabled ? "Enabling" : "Disabling");
1290
+ force_mono = enabled;
1236
1291
 
1292
+ // Loop over existing sources and update the force mono flags.
1237
1293
  for (const auto& kv : sources) {
1238
1294
  const std::string& name = kv.first;
1239
1295
  obs_source_t* source = kv.second;
1240
1296
 
1241
1297
  if (!source) {
1242
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1298
+ blog(LOG_WARNING, "Source %s not found when setting force mono", name.c_str());
1243
1299
  continue;
1244
1300
  }
1245
1301
 
1246
1302
  const char* type = obs_source_get_id(source);
1247
1303
 
1248
- if (strcmp(type, AUDIO_INPUT) == 0) {
1249
- obs_source_set_volume(source, input_volume);
1304
+ if (strcmp(type, AUDIO_INPUT) != 0) {
1305
+ // Force mono is only applicable to microphones, skip other types.
1306
+ continue;
1307
+ }
1308
+
1309
+ if (enabled) {
1310
+ blog(LOG_INFO, "Setting force mono flag on source %s", name.c_str());
1311
+ uint32_t flags = obs_source_get_flags(source);
1312
+ obs_source_set_flags(source, flags | OBS_SOURCE_FLAG_FORCE_MONO);
1313
+ } else {
1314
+ blog(LOG_INFO, "Unsetting force mono flag on source %s", name.c_str());
1315
+ uint32_t flags = obs_source_get_flags(source);
1316
+ obs_source_set_flags(source, flags & ~OBS_SOURCE_FLAG_FORCE_MONO);
1250
1317
  }
1251
1318
  }
1252
1319
  }
1253
1320
 
1254
- void ObsInterface::setProcessVolume(float volume) {
1255
- blog(LOG_INFO, "Setting process volume to %f", volume);
1256
- process_volume = volume;
1321
+ void ObsInterface::setAudioSuppression(bool enabled) {
1322
+ blog(LOG_INFO, "%s audio suppression on all input devices", enabled ? "Enabling" : "Disabling");
1323
+ audio_suppression = enabled;
1257
1324
 
1325
+ // Loop over existing sources and add filters to any that need it.
1258
1326
  for (const auto& kv : sources) {
1259
1327
  const std::string& name = kv.first;
1260
1328
  obs_source_t* source = kv.second;
1261
1329
 
1262
1330
  if (!source) {
1263
- blog(LOG_WARNING, "Source %s not found", name.c_str());
1331
+ blog(LOG_WARNING, "Source %s not found when adding filters", name.c_str());
1264
1332
  continue;
1265
1333
  }
1266
1334
 
1267
1335
  const char* type = obs_source_get_id(source);
1268
1336
 
1269
- if (strcmp(type, AUDIO_PROCESS) == 0) {
1270
- obs_source_set_volume(source, process_volume);
1337
+ if (strcmp(type, AUDIO_INPUT) != 0) {
1338
+ // Don't care about non-input sources. This is purely for suppressing
1339
+ // microphone background noise.
1340
+ continue;
1341
+ }
1342
+
1343
+ // Check for a filter existing and add or remove it as appropriate.
1344
+ auto filter_it = filters.find(name);
1345
+
1346
+ if (audio_suppression && filter_it == filters.end()) {
1347
+ blog(LOG_INFO, "Setting up filter for source: %s", name.c_str());
1348
+
1349
+ std::string filter_name = "Filter for " + name;
1350
+
1351
+ obs_source_t *filter = obs_source_create(
1352
+ "noise_suppress_filter_v2",
1353
+ filter_name.c_str(),
1354
+ nullptr, // Defaults are sensible.
1355
+ nullptr
1356
+ );
1357
+
1358
+ if (!filter) {
1359
+ blog(LOG_ERROR, "Failed to create filter for source: %s", name.c_str());
1360
+ throw std::runtime_error("Failed to create filter!");
1361
+ }
1362
+
1363
+ filters[name] = filter;
1364
+ obs_source_filter_add(source, filter);
1365
+ } else if (!audio_suppression && filter_it != filters.end()) {
1366
+ blog(LOG_INFO, "Removing filters for source: %s", name.c_str());
1367
+ obs_source_t* filter = filter_it->second;
1368
+ obs_source_filter_remove(source, filter);
1369
+ filters.erase(name);
1370
+ obs_source_release(filter);
1271
1371
  }
1272
1372
  }
1273
1373
  }
1274
1374
 
1275
- void ObsInterface::setVolmeterEnabled(bool enabled) {
1276
- blog(LOG_INFO, "Setting volmeter enabled: %d", enabled);
1277
- volmeter_enabled = enabled;
1278
- }
1375
+ void ObsInterface::sourceCallback(std::string name) {
1376
+ blog(LOG_INFO, "Source callback triggered for %s", name.c_str());
1377
+ SignalData* sd = new SignalData{ "source", name.c_str(), 0 };
1378
+ jscb.NonBlockingCall(sd, call_jscb);
1379
+ }
1380
+
1381
+ void ObsInterface::zeroVolmeter(std::string name) {
1382
+ blog(LOG_INFO, "Zeroing volmeter for %s", name.c_str());
1383
+ SignalData* sd = new SignalData{ "volmeter", name.c_str(), 0, 0 };
1384
+ jscb.NonBlockingCall(sd, call_jscb);
1385
+ }