| 45 | } |
| 46 | |
| 47 | start(): void { |
| 48 | this.stopped = false; |
| 49 | this.spawnProc(); |
| 50 | if (!this.watchdog) { |
| 51 | this.watchdog = setInterval(() => { |
| 52 | if (this.stopped) return; |
| 53 | // Keepalive: during gaps, re-push the last frame so the multipart |
| 54 | // connections stay warm instead of idling out. |
| 55 | if (this.last && Date.now() - this.lastFrameAt > 2500) { |
| 56 | for (const res of this.clients) this.pushFrame(res, this.last); |
| 57 | } |
| 58 | if (!this.proc) return; |
| 59 | if (this.lastFrameAt && Date.now() - this.lastFrameAt > VideoStream.STALL_MS) { |
| 60 | console.error("[video] stream stalled — restarting ffmpeg"); |
| 61 | this.lastError = "stream stalled; reconnecting"; |
| 62 | this.lastFrameAt = 0; |
| 63 | this.kill(); // exit handler respawns |
| 64 | this.restartTimer = setTimeout(() => this.spawnProc(), 1000); |
| 65 | } |
| 66 | }, 2000); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | stop(): void { |
| 71 | this.stopped = true; |