summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-client/src/cast-relay.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-client/src/cast-relay.js')
-rw-r--r--packages/meshbay-client/src/cast-relay.js53
1 files changed, 38 insertions, 15 deletions
diff --git a/packages/meshbay-client/src/cast-relay.js b/packages/meshbay-client/src/cast-relay.js
index 71c07a4..58eb9ac 100644
--- a/packages/meshbay-client/src/cast-relay.js
+++ b/packages/meshbay-client/src/cast-relay.js
@@ -19,7 +19,7 @@
* · Bind to the LAN interface, never 0.0.0.0
* · Fixed port range (19550-19553), opened only during active cast
* · Unguessable token in the URL (32 hex chars)
- * · CORS is granted on the subtitle path only, and still behind the token
+ * · CORS is granted on both served paths, and both stay behind the token
* · Cache-Control: no-store on every response
* · Server destroyed when playback stops — zero residual surface
*/
@@ -29,6 +29,25 @@
const crypto = require('node:crypto');
const http = require('node:http');
const os = require('node:os');
+const util = require('node:util');
+
+/**
+ * Progress logging, off unless asked for:
+ *
+ * NODE_DEBUG=cast-relay npm start
+ *
+ * Most of what this class has to say repeats without bound — a line every
+ * fiftieth fragment for the length of a film, one per dropped fragment
+ * whenever a client falls behind — and it lands in the terminal the app was
+ * started from. Node's own `debuglog` is used rather than a flag of our own:
+ * it costs nothing when disabled, since the message is never even formatted.
+ *
+ * Nothing that stops a cast is hidden behind it. Every hard failure here
+ * throws — no free port, no such file — and the renderer already reports the
+ * rejection. The one fault that neither throws nor reaches anybody else is a
+ * loss of fMP4 framing, and that one stays on `console.warn`.
+ */
+const debug = util.debuglog('cast-relay');
const RING_CAP = 64;
const BACKPRESSURE_HIGH = 8 * 1024 * 1024;
@@ -71,7 +90,7 @@ class BoxAccumulator {
if (!this._synced) {
const idx = this._findMoof();
if (idx === -1) return fragments;
- console.log(`[cast-relay] box sync: found first moof at byte offset ${idx}, discarded ${idx} bytes`);
+ debug(`[cast-relay] box sync: found first moof at byte offset ${idx}, discarded ${idx} bytes`);
this._buf = this._buf.subarray(idx);
this._synced = true;
}
@@ -81,7 +100,11 @@ class BoxAccumulator {
const type = this._buf.readUInt32BE(4);
if (size < 8) {
- console.log(`[cast-relay] box sync lost: invalid size ${size}, rescanning`);
+ // Not `debug`: the byte stream stopped being framed fMP4. It recovers
+ // by rescanning, so nothing throws and nobody else ever hears about
+ // it — this line is the only trace that the picture on the television
+ // is missing a piece.
+ console.warn(`[cast-relay] box sync lost: invalid size ${size}, rescanning`);
this._synced = false;
const idx = this._findMoof();
if (idx === -1) return fragments;
@@ -182,7 +205,7 @@ class CastRelay {
if (!sub || !sub.vtt) {
this._subtitle = null;
this._subtitleVersion++;
- console.log('[cast-relay] subtitle cleared');
+ debug('[cast-relay] subtitle cleared');
return null;
}
this._subtitle = {
@@ -191,7 +214,7 @@ class CastRelay {
label: sub.label || '',
};
this._subtitleVersion++;
- console.log(`[cast-relay] subtitle set: ${this._subtitle.vtt.length} bytes`
+ debug(`[cast-relay] subtitle set: ${this._subtitle.vtt.length} bytes`
+ `, lang "${this._subtitle.language}", v${this._subtitleVersion}`);
return this.subtitle;
}
@@ -232,14 +255,14 @@ class CastRelay {
bound = true;
} catch (err) {
if (err.code !== 'EADDRINUSE') throw err;
- console.log(`[cast-relay] port ${port} busy, trying next`);
+ debug(`[cast-relay] port ${port} busy, trying next`);
}
}
if (!bound) throw new Error('All cast relay ports are in use');
this._server = server;
- console.log(`[cast-relay] started on ${this.url}`);
- console.log(`[cast-relay] init segment: ${this._initSegment ? this._initSegment.length + ' bytes' : 'none'}`);
+ debug(`[cast-relay] started on ${this.url}`);
+ debug(`[cast-relay] init segment: ${this._initSegment ? this._initSegment.length + ' bytes' : 'none'}`);
return {
url: this.url,
port: this._port,
@@ -257,7 +280,7 @@ class CastRelay {
for (const frag of fragments) {
this._fragCount++;
if (this._fragCount <= 3 || this._fragCount % 50 === 0) {
- console.log(`[cast-relay] fragment #${this._fragCount}: ${frag.length} bytes (from ${this._chunkCount} chunks), ${this._clients.size} client(s)`);
+ debug(`[cast-relay] fragment #${this._fragCount}: ${frag.length} bytes (from ${this._chunkCount} chunks), ${this._clients.size} client(s)`);
}
if (this._ring.length >= RING_CAP) {
@@ -267,7 +290,7 @@ class CastRelay {
for (const res of this._clients) {
if (res.writableLength > BACKPRESSURE_HIGH) {
- console.log(`[cast-relay] backpressure: dropping fragment for slow client`);
+ debug(`[cast-relay] backpressure: dropping fragment for slow client`);
continue;
}
res.write(frag);
@@ -277,14 +300,14 @@ class CastRelay {
}
finish() {
- console.log('[cast-relay] finishing stream');
+ debug('[cast-relay] finishing stream');
for (const res of this._clients) {
try { res.end(); } catch { /* already closed */ }
}
}
async stop() {
- console.log(`[cast-relay] stopping — ${this._chunkCount} chunks, ${this._fragCount} fragments, ${(this._bytesSent / 1048576).toFixed(1)} MB sent`);
+ debug(`[cast-relay] stopping — ${this._chunkCount} chunks, ${this._fragCount} fragments, ${(this._bytesSent / 1048576).toFixed(1)} MB sent`);
for (const res of this._clients) {
try { res.end(); } catch { /* already closed */ }
}
@@ -372,12 +395,12 @@ class CastRelay {
sent += frag.length;
}
- console.log(`[cast-relay] client connected from ${req.socket.remoteAddress} — sent init + ${this._ring.length} fragments (${(sent / 1024).toFixed(0)} KB)`);
+ debug(`[cast-relay] client connected from ${req.socket.remoteAddress} — sent init + ${this._ring.length} fragments (${(sent / 1024).toFixed(0)} KB)`);
this._clients.add(res);
req.on('close', () => {
this._clients.delete(res);
- console.log(`[cast-relay] client disconnected, ${this._clients.size} remaining`);
+ debug(`[cast-relay] client disconnected, ${this._clients.size} remaining`);
});
}
@@ -394,7 +417,7 @@ class CastRelay {
'Cache-Control': 'no-store',
});
res.end(this._subtitle.vtt);
- console.log(`[cast-relay] subtitle served: ${this._subtitle.vtt.length} bytes`);
+ debug(`[cast-relay] subtitle served: ${this._subtitle.vtt.length} bytes`);
}
}