remoterigs-raspberrypi 0.1.13 → 0.1.15

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "remoterigs-raspberrypi",
3
3
  "type": "module",
4
- "version": "0.1.13",
4
+ "version": "0.1.15",
5
5
  "main": "./index.js",
6
6
  "files": [
7
7
  "index.js",
@@ -1,9 +1,13 @@
1
- import { spawn } from 'child_process';
1
+ import { spawn, spawnSync } from 'child_process';
2
2
  import axios from 'axios';
3
3
  import { LogSettingType, VideoType, WebRTCMessage } from '../models/models.js';
4
4
  import SignalR from './signalr.js';
5
5
  import Debug from './debug.js';
6
6
  export default class WebRTCStreamerWrapper {
7
+ // one webrtc-streamer process for the whole client: every sign in creates new components (and wrappers),
8
+ // the process is only restarted when the TURN credential changed
9
+ static { this.sharedProcess = null; }
10
+ static { this.sharedTurnServer = null; }
7
11
  constructor(component, clientRTCConfigurationViewModel, videoType) {
8
12
  this.component = component;
9
13
  this.clientRTCConfigurationViewModel = clientRTCConfigurationViewModel;
@@ -41,6 +45,10 @@ export default class WebRTCStreamerWrapper {
41
45
  }
42
46
  // Stop webrtc-streamer process
43
47
  if (this.webrtcStreamerProcess) {
48
+ if (WebRTCStreamerWrapper.sharedProcess === this.webrtcStreamerProcess) {
49
+ WebRTCStreamerWrapper.sharedProcess = null;
50
+ WebRTCStreamerWrapper.sharedTurnServer = null;
51
+ }
44
52
  this.webrtcStreamerProcess.kill();
45
53
  this.webrtcStreamerProcess = null;
46
54
  }
@@ -92,42 +100,102 @@ export default class WebRTCStreamerWrapper {
92
100
  }
93
101
  }
94
102
  async StartWebRTCStreamer() {
95
- try {
96
- const response = await this.streamerClient.get('/api/version');
97
- this.Log("webrtc-streamer is already running: " + JSON.stringify(response));
98
- return;
103
+ const turnServer = this.clientRTCConfigurationViewModel.turnServer ?? "";
104
+ const running = WebRTCStreamerWrapper.sharedProcess;
105
+ if (running != null && running.exitCode === null && running.signalCode === null) {
106
+ if (WebRTCStreamerWrapper.sharedTurnServer === turnServer) {
107
+ this.Log("webrtc-streamer is already running with the current TURN credential");
108
+ this.webrtcStreamerProcess = running;
109
+ return;
110
+ }
111
+ // a new sign in brought a new TURN credential (the old one may have been deleted at Metered)
112
+ this.Log("TURN credential changed, restarting webrtc-streamer...");
113
+ await WebRTCStreamerWrapper.StopProcess(running);
99
114
  }
100
- catch (err) {
115
+ else if (await this.IsStreamerResponding()) {
116
+ // started by someone else: its TURN credential is unknown, so restart it with the current one
117
+ this.Log("webrtc-streamer is running from an unknown streamerProcess, restarting it with the current TURN credential...");
118
+ spawnSync("pkill", ["-x", "webrtc-streamer"]);
119
+ await this.WaitUntilStreamerStopped();
120
+ }
121
+ else {
101
122
  this.Log("webrtc-streamer not running, starting it...");
102
123
  }
124
+ const stunServer = this.clientRTCConfigurationViewModel.stunServer ?? "";
103
125
  const args = [
104
126
  "-H", `0.0.0.0:${this.STREAMER_PORT}`,
105
- "-s",
106
- //"--stun", this.clientRTCConfigurationViewModel.stunServer,
107
- "--turn", this.clientRTCConfigurationViewModel.turnServer
127
+ // "-s<host:port>" (no space): our own STUN server, so the rig also has a public (srflx) candidate;
128
+ // "-s-" = no STUN
129
+ stunServer != "" ? `-s${stunServer}` : "-s-",
108
130
  ];
131
+ if (turnServer != "") {
132
+ args.push("--turn", turnServer);
133
+ }
134
+ else {
135
+ this.Log("no TURN credential received from the server, starting webrtc-streamer without TURN");
136
+ }
109
137
  const command = "webrtc-streamer";
110
- this.webrtcStreamerProcess = spawn(command, args);
111
- if (this.webrtcStreamerProcess != null) {
112
- if (this.webrtcStreamerProcess.stdout != null) {
113
- this.webrtcStreamerProcess.stdout.on("data", (data) => {
114
- this.Log(`webrtc-streamer stdout: ${data}`);
115
- });
116
- }
117
- if (this.webrtcStreamerProcess.stderr != null) {
118
- this.webrtcStreamerProcess.stderr.on("data", (data) => {
119
- this.Log(`webrtc-streamer stderr: ${data}`);
120
- });
121
- }
122
- this.webrtcStreamerProcess.on("error", (err) => {
123
- this.Error(`webrtc-streamer error: ${JSON.stringify(err)}`);
138
+ const streamerProcess = spawn(command, args);
139
+ this.webrtcStreamerProcess = streamerProcess;
140
+ WebRTCStreamerWrapper.sharedProcess = streamerProcess;
141
+ WebRTCStreamerWrapper.sharedTurnServer = turnServer;
142
+ if (streamerProcess.stdout != null) {
143
+ streamerProcess.stdout.on("data", (data) => {
144
+ this.Log(`webrtc-streamer stdout: ${data}`);
124
145
  });
125
- this.webrtcStreamerProcess.on("close", (code) => {
126
- this.Log(`webrtc-streamer exited with code ${code}`);
146
+ }
147
+ if (streamerProcess.stderr != null) {
148
+ streamerProcess.stderr.on("data", (data) => {
149
+ this.Log(`webrtc-streamer stderr: ${data}`);
127
150
  });
128
151
  }
152
+ streamerProcess.on("error", (err) => {
153
+ this.Error(`webrtc-streamer error: ${JSON.stringify(err)}`);
154
+ });
155
+ streamerProcess.on("close", (code) => {
156
+ this.Log(`webrtc-streamer exited with code ${code}`);
157
+ if (WebRTCStreamerWrapper.sharedProcess === streamerProcess) {
158
+ WebRTCStreamerWrapper.sharedProcess = null;
159
+ WebRTCStreamerWrapper.sharedTurnServer = null;
160
+ }
161
+ });
129
162
  this.Log("Spawned webrtc-streamer process: " + command);
130
163
  }
164
+ async IsStreamerResponding() {
165
+ try {
166
+ await this.streamerClient.get('/api/version');
167
+ return true;
168
+ }
169
+ catch (err) {
170
+ return false;
171
+ }
172
+ }
173
+ async WaitUntilStreamerStopped() {
174
+ for (let i = 0; i < 20; i++) {
175
+ if (!(await this.IsStreamerResponding())) {
176
+ return;
177
+ }
178
+ await new Promise(resolve => setTimeout(resolve, 250));
179
+ }
180
+ this.Error("webrtc-streamer did not stop within 5 seconds");
181
+ }
182
+ static StopProcess(streamerProcess) {
183
+ return new Promise(resolve => {
184
+ if (streamerProcess.exitCode !== null || streamerProcess.signalCode !== null) {
185
+ resolve();
186
+ return;
187
+ }
188
+ const timer = setTimeout(() => {
189
+ streamerProcess.kill("SIGKILL");
190
+ resolve();
191
+ }, 5000);
192
+ streamerProcess.once("exit", () => {
193
+ clearTimeout(timer);
194
+ resolve();
195
+ });
196
+ streamerProcess.kill();
197
+ });
198
+ }
131
199
  async WaitForStreamer() {
132
200
  const maxRetries = 20;
133
201
  const retryDelay = 500;