noobs 0.0.187 → 0.0.200

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,6 +93,8 @@ npm run build # compile it
93
93
 
94
94
  npm version patch # version bump
95
95
  npm pack # build tgz locally
96
+
97
+ npm login
96
98
  npm publish # publish to npm
97
99
  ```
98
100
 
package/dist/bin/obs.dll CHANGED
Binary file
package/dist/noobs.node CHANGED
Binary file
Binary file
package/index.d.ts CHANGED
@@ -171,6 +171,7 @@ interface Noobs {
171
171
 
172
172
  // Recording functions.
173
173
  SetBuffering(buffering: boolean): void; // In buffering mode, the recording is stored in memory and can be converted to a file later.
174
+ SetFragmentation(fragmented: boolean): void; // Use fragmented MP4.
174
175
  StartBuffer(): void;
175
176
  StartRecording(offset: number): void;
176
177
  StopRecording(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "noobs",
3
- "version": "0.0.187",
3
+ "version": "0.0.200",
4
4
  "description": "A native Node.js addon with libobs bindings for Warcraft Recorder.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/src/main.cpp CHANGED
@@ -156,6 +156,28 @@ Napi::Value ObsSetBuffering(const Napi::CallbackInfo& info) {
156
156
  return info.Env().Undefined();
157
157
  }
158
158
 
159
+ Napi::Value ObsSetFragmentation(const Napi::CallbackInfo& info) {
160
+ blog(LOG_INFO, "ObsSetFragmentation called");
161
+
162
+ if (!obs) {
163
+ blog(LOG_ERROR, "ObsSetFragmentation called but obs is not initialized");
164
+ Napi::Error::New(info.Env(), "Obs not initialized").ThrowAsJavaScriptException();
165
+ return info.Env().Undefined();
166
+ }
167
+
168
+ bool valid = info.Length() == 1 && info[0].IsBoolean();
169
+
170
+ if (!valid) {
171
+ Napi::TypeError::New(info.Env(), "Invalid arguments passed to ObsSetFragmentation").ThrowAsJavaScriptException();
172
+ return info.Env().Undefined();
173
+ }
174
+
175
+ bool fragmented = info[0].As<Napi::Boolean>().Value();
176
+ obs->setFragmentation(fragmented);
177
+
178
+ return info.Env().Undefined();
179
+ }
180
+
159
181
  Napi::Value ObsStartBuffer(const Napi::CallbackInfo& info) {
160
182
  blog(LOG_INFO, "ObsStartBuffer called");
161
183
 
@@ -678,6 +700,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
678
700
  exports.Set("SetVideoEncoder", Napi::Function::New(env, ObsSetVideoEncoder));
679
701
 
680
702
  exports.Set("SetBuffering", Napi::Function::New(env, ObsSetBuffering));
703
+ exports.Set("SetFragmentation", Napi::Function::New(env, ObsSetFragmentation));
681
704
  exports.Set("StartBuffer", Napi::Function::New(env, ObsStartBuffer));
682
705
  exports.Set("StartRecording", Napi::Function::New(env, ObsStartRecording));
683
706
  exports.Set("StopRecording", Napi::Function::New(env, ObsStopRecording));
@@ -23,6 +23,10 @@ void call_jscb(Napi::Env env, Napi::Function cb, SignalData* sd) {
23
23
  obj.Set("error", Napi::String::New(env, sd->error.value()));
24
24
  }
25
25
 
26
+ if (sd->path.has_value()) {
27
+ obj.Set("path", Napi::String::New(env, sd->path.value()));
28
+ }
29
+
26
30
  cb.Call({ obj });
27
31
  delete sd;
28
32
  }
@@ -271,6 +275,12 @@ void ObsInterface::create_output() {
271
275
  unbuffered_output_filename = filename;
272
276
  }
273
277
 
278
+ if (fragmented && file_extension == "mp4") {
279
+ blog(LOG_INFO, "Fragmentation enabled");
280
+ std::string mux_frag = "movflags=frag_keyframe+empty_moov+delay_moov";
281
+ obs_data_set_string(settings, "muxer_settings", mux_frag.c_str());
282
+ }
283
+
274
284
  obs_output_update(output, settings);
275
285
  obs_data_release(settings);
276
286
  connect_signal_handlers(output);
@@ -591,25 +601,40 @@ obs_properties_t* ObsInterface::getSourceProperties(std::string name) {
591
601
  }
592
602
 
593
603
  void ObsInterface::output_signal_handler(void *data, calldata_t *cd) {
594
- long long code = calldata_int(cd, "code");
595
- const char *err = calldata_string(cd, "last_error");
604
+ SignalContext* ctx = static_cast<SignalContext*>(data);
605
+ ObsInterface* self = ctx->self;
606
+ SignalData* sd;
596
607
 
597
- std::optional<std::string> error;
608
+ blog(LOG_INFO, "Handling %s signal from libobs", ctx->id.c_str());
598
609
 
599
- if (err) {
600
- error = std::string(err);
601
- }
610
+ if (ctx->id == "converted") {
611
+ const char *path = calldata_string(cd, "file");
602
612
 
603
- SignalContext* ctx = static_cast<SignalContext*>(data);
604
- ObsInterface* self = ctx->self;
613
+ sd = new SignalData{
614
+ "output",
615
+ "converted",
616
+ 0, // Never actually get a code for a converted signal, so just set it to 0.
617
+ std::nullopt, // No value, that's only used for volmeters.
618
+ std::nullopt, // Never expect errors here.
619
+ std::string(path),
620
+ };
621
+ } else {
622
+ long long code = calldata_int(cd, "code");
623
+ const char *err = calldata_string(cd, "last_error");
624
+ std::optional<std::string> error;
605
625
 
606
- SignalData* sd = new SignalData{
607
- "output",
608
- ctx->id.c_str(),
609
- code,
610
- std::nullopt, // No value, that's only used for volmeters.
611
- error,
612
- };
626
+ if (err) {
627
+ error = std::string(err);
628
+ }
629
+
630
+ sd = new SignalData{
631
+ "output",
632
+ ctx->id.c_str(),
633
+ code,
634
+ std::nullopt, // No value, that's only used for volmeters.
635
+ error,
636
+ };
637
+ }
613
638
 
614
639
  self->jscb.NonBlockingCall(sd, call_jscb);
615
640
  }
@@ -622,6 +647,7 @@ void ObsInterface::connect_signal_handlers(obs_output_t *output) {
622
647
  signal_handler_connect(sh, "stop", output_signal_handler, stop_ctx);
623
648
  signal_handler_connect(sh, "activate", output_signal_handler, activate_ctx);
624
649
  signal_handler_connect(sh, "deactivate", output_signal_handler, deactivate_ctx);
650
+ signal_handler_connect(sh, "converted", output_signal_handler, converted_ctx);
625
651
  }
626
652
 
627
653
  void ObsInterface::disconnect_signal_handlers(obs_output_t *output) {
@@ -631,7 +657,8 @@ void ObsInterface::disconnect_signal_handlers(obs_output_t *output) {
631
657
  signal_handler_disconnect(sh, "stopping", output_signal_handler, stopping_ctx);
632
658
  signal_handler_disconnect(sh, "stop", output_signal_handler, stop_ctx);
633
659
  signal_handler_disconnect(sh, "activate", output_signal_handler, activate_ctx);
634
- signal_handler_disconnect(sh, "deactivate ", output_signal_handler, deactivate_ctx);
660
+ signal_handler_disconnect(sh, "deactivate", output_signal_handler, deactivate_ctx);
661
+ signal_handler_disconnect(sh, "converted ", output_signal_handler, converted_ctx);
635
662
  }
636
663
 
637
664
  bool draw_source_outline(obs_scene_t *scene, obs_sceneitem_t *item, void *p) {
@@ -949,6 +976,7 @@ ObsInterface::ObsInterface(
949
976
  stop_ctx = new SignalContext{ this, "stop" };
950
977
  activate_ctx = new SignalContext{this, "activate"};
951
978
  deactivate_ctx = new SignalContext{this, "deactivate"};
979
+ converted_ctx = new SignalContext{this, "converted"};
952
980
 
953
981
  // Create the resources we rely on.
954
982
  create_scene();
@@ -981,6 +1009,7 @@ ObsInterface::~ObsInterface() {
981
1009
  delete stop_ctx;
982
1010
  delete activate_ctx;
983
1011
  delete deactivate_ctx;
1012
+ delete converted_ctx;
984
1013
 
985
1014
  for (auto& kv : sources) {
986
1015
  std::string name = kv.first;
@@ -1047,6 +1076,16 @@ void ObsInterface::setBuffering(bool value) {
1047
1076
  create_output();
1048
1077
  }
1049
1078
 
1079
+ void ObsInterface::setFragmentation(bool value) {
1080
+ if (obs_output_active(output)) {
1081
+ blog(LOG_ERROR, "Cannot change fragmentation state while output is active");
1082
+ throw new std::runtime_error("Cannot change fragmentation state while output is active");
1083
+ }
1084
+
1085
+ fragmented = value;
1086
+ create_output();
1087
+ }
1088
+
1050
1089
  void ObsInterface::startBuffering() {
1051
1090
  blog(LOG_INFO, "ObsInterface::startBuffering called");
1052
1091
 
@@ -19,6 +19,7 @@ struct SignalData {
19
19
  long long code;
20
20
  std::optional<float> value;
21
21
  std::optional<std::string> error;
22
+ std::optional<std::string> path; // used by converted signal handler
22
23
  };
23
24
 
24
25
  struct SignalContext {
@@ -52,6 +53,7 @@ class ObsInterface {
52
53
  void forceStopRecording(); // Force stop the recording, this will not save the current recording.
53
54
  std::string getLastRecording(); // Get the last recorded file path.
54
55
  void setBuffering(bool buffer); // Enable or disable buffering.
56
+ void setFragmentation(bool fragmented); // Enable or disable fragmented MP4.
55
57
  void setRecordingCfg(const std::string& recordingPath, const std::string& fileExtension); // Set the recording path.
56
58
  void setVideoContext(int fps, int width, int height); // Reset video settings.
57
59
 
@@ -108,6 +110,7 @@ class ObsInterface {
108
110
  std::string file_extension = "mp4"; // File extension for recordings.
109
111
 
110
112
  bool buffering = false; // Whether we are buffering the recording in memory.
113
+ bool fragmented = false; // Use fragmented MP4.
111
114
  bool drawSourceOutline = false; // Draw red outline around source
112
115
  void init_obs(const std::string& distPath);
113
116
  int reset_video(int fps, int width, int height);
@@ -122,6 +125,7 @@ class ObsInterface {
122
125
  SignalContext* stop_ctx;
123
126
  SignalContext* activate_ctx;
124
127
  SignalContext* deactivate_ctx;
128
+ SignalContext* converted_ctx;
125
129
  static void output_signal_handler(void *data, calldata_t *cd);
126
130
 
127
131
  void list_encoders(obs_encoder_type type = OBS_ENCODER_VIDEO);