@torrent-tv/proxy 2.9.3 → 2.9.5
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 2.9.5
|
|
2
|
+
|
|
3
|
+
- **Fix**: Segment files are now read with a 4 MB `highWaterMark` (`hls-session-manager.js` `getFileStream`) so the body is delivered in few, large chunks. On a busy ARM host the in-process WebTorrent hashing starves the Node event loop in bursts while the first segments are served; reading in fewer iterations cuts the time lost between chunks (the first segment previously transferred in ~79 × 43 KB reads spaced ~610 ms apart).
|
|
4
|
+
|
|
5
|
+
## 2.9.4
|
|
6
|
+
|
|
7
|
+
- **Chore**: Temporary `[net-debug]` instrumentation in `data-channel-handler.js` now splits transfer timing into `fetchMs` (waiting for the local route, incl. ffmpeg segment finalization), `ttfbMs` (time to first body chunk), `sendMs` (channel send duration) and `chunks`, to locate where early-segment latency is spent (transport vs segment production).
|
|
8
|
+
|
|
1
9
|
## 2.9.3
|
|
2
10
|
|
|
3
11
|
- **New**: WebRTC data-channel response bodies are now sent as **binary** frames (`sendMessageBinary`) instead of base64-encoded JSON `response-chunk` messages, removing the ~33% base64 overhead and the JSON encode cost. Frame layout: `[flags(1)][idLen(1)][requestId(ASCII)][payload]`. Control messages (`response-start`, `response-error`, `pong`) remain JSON strings. Requires the matching browser client (server ≥ 0.8.0); **deploy the server before the proxy**.
|
package/package.json
CHANGED
|
@@ -162,6 +162,10 @@ export function createDataChannelHandler({ proxyPort, onLog }) {
|
|
|
162
162
|
const requestHeaders = { ...(forwardedHeaders ?? {}), host: `127.0.0.1:${proxyPort}` };
|
|
163
163
|
|
|
164
164
|
let response;
|
|
165
|
+
// [net-debug] TEMPORARY: time spent in the local fetch (waiting for the
|
|
166
|
+
// route to return a response — e.g. long-polling until an HLS segment is
|
|
167
|
+
// finalized by ffmpeg) vs. the body transfer over the data channel.
|
|
168
|
+
const fetchStartedAt = Date.now();
|
|
165
169
|
try {
|
|
166
170
|
response = await fetch(targetUrl, {
|
|
167
171
|
method,
|
|
@@ -195,7 +199,13 @@ export function createDataChannelHandler({ proxyPort, onLog }) {
|
|
|
195
199
|
try {
|
|
196
200
|
const reader = response.body.getReader();
|
|
197
201
|
// [net-debug] TEMPORARY: measure transfer size/time and channel buffering.
|
|
202
|
+
// fetchMs = time waiting for the route (incl. ffmpeg segment finalization).
|
|
203
|
+
// ttfbMs = time from body-read start to the first chunk with data (loopback).
|
|
204
|
+
// sendMs = total body read+send duration over the data channel.
|
|
205
|
+
const fetchMs = Date.now() - fetchStartedAt;
|
|
198
206
|
const sendStartedAt = Date.now();
|
|
207
|
+
let firstByteMs = -1;
|
|
208
|
+
let chunks = 0;
|
|
199
209
|
let totalBytes = 0;
|
|
200
210
|
let maxBuffered = 0;
|
|
201
211
|
while (true) {
|
|
@@ -206,11 +216,14 @@ export function createDataChannelHandler({ proxyPort, onLog }) {
|
|
|
206
216
|
let bufferedNow = 0;
|
|
207
217
|
try { bufferedNow = typeof channel.bufferedAmount === "function" ? channel.bufferedAmount() : 0; } catch { /* ignore */ }
|
|
208
218
|
log(
|
|
209
|
-
`[net-debug] sent ${path}${queryInfo} bytes=${totalBytes}
|
|
219
|
+
`[net-debug] sent ${path}${queryInfo} bytes=${totalBytes} fetchMs=${fetchMs} ` +
|
|
220
|
+
`ttfbMs=${firstByteMs} sendMs=${elapsedMs} chunks=${chunks} ` +
|
|
210
221
|
`maxBuffered=${maxBuffered} bufferedAtEnd=${bufferedNow}`
|
|
211
222
|
);
|
|
212
223
|
break;
|
|
213
224
|
}
|
|
225
|
+
if (firstByteMs < 0) firstByteMs = Date.now() - sendStartedAt;
|
|
226
|
+
chunks += 1;
|
|
214
227
|
totalBytes += value.length;
|
|
215
228
|
try {
|
|
216
229
|
const b = typeof channel.bufferedAmount === "function" ? channel.bufferedAmount() : 0;
|
|
@@ -33,6 +33,11 @@ const DEFAULT_SESSION_TTL_MS = 120 * 1000;
|
|
|
33
33
|
const DEFAULT_STARTUP_WAIT_MS = 5_000;
|
|
34
34
|
const MICROSECONDS_PER_SECOND = 1_000_000;
|
|
35
35
|
const PROGRESS_LOG_INTERVAL_MS = 5_000;
|
|
36
|
+
// Read segment files in large blocks so the body is delivered to the data
|
|
37
|
+
// channel in few, big chunks. On a busy ARM host the in-process WebTorrent
|
|
38
|
+
// hashing starves the event loop in bursts, so fewer read iterations means
|
|
39
|
+
// far less time lost between chunks while serving the first segments.
|
|
40
|
+
const SEGMENT_READ_HIGH_WATER_MARK = 4 * 1024 * 1024;
|
|
36
41
|
|
|
37
42
|
/**
|
|
38
43
|
* Resolve after a given number of milliseconds.
|
|
@@ -899,9 +904,12 @@ export class HlsSessionManager {
|
|
|
899
904
|
const filePath = path.join(session.dirPath, fileName);
|
|
900
905
|
try {
|
|
901
906
|
await access(filePath);
|
|
907
|
+
const isPlaylist = fileName === PLAYLIST_FILE_NAME;
|
|
902
908
|
return {
|
|
903
909
|
kind: "file",
|
|
904
|
-
stream:
|
|
910
|
+
stream: isPlaylist
|
|
911
|
+
? createReadStream(filePath)
|
|
912
|
+
: createReadStream(filePath, { highWaterMark: SEGMENT_READ_HIGH_WATER_MARK }),
|
|
905
913
|
contentType:
|
|
906
914
|
fileName === PLAYLIST_FILE_NAME
|
|
907
915
|
? "application/vnd.apple.mpegurl"
|