@torrent-tv/proxy 2.9.9 → 2.9.11
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 +9 -1
- package/package.json +1 -1
- package/routes/stream/get.js +5 -0
- package/services/hwaccel.js +5 -2
- package/services/torrent-pool.js +46 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
## 2.9.
|
|
1
|
+
## 2.9.11
|
|
2
|
+
|
|
3
|
+
- **New**: Seek-aware torrent piece prioritization. On every `/stream` range request the proxy now marks the torrent pieces at the read position **critical** (`TorrentPool.prioritizeByteRange` → `torrent.critical`, ~8 MB window). After a seek, ffmpeg opens the input at a new byte offset; previously those pieces waited behind the sequential download backlog, so seeking into an undownloaded region stalled ~15-18 s while the proxy fetched data. Now the seek position jumps the download queue.
|
|
4
|
+
|
|
5
|
+
## 2.9.10
|
|
6
|
+
|
|
7
|
+
- **Fix**: Raised the adaptive-preset speed margin (`PRESET_SPEED_MARGIN` 1.3 → 1.8). The preset benchmark runs at startup with an idle CPU, but during playback ffmpeg competes with in-process WebTorrent (download + SHA1 hashing) and delivery, so real throughput is lower than benchmarked. A 1.3× margin picked a preset that ran near/below realtime under load (e.g. `faster` at ~1.3×) and stalled; 1.8× picks a preset with genuine headroom (e.g. `veryfast`), keeping playback above 1× under real load.
|
|
8
|
+
|
|
9
|
+
## 2.9.9
|
|
2
10
|
|
|
3
11
|
- **Fix**: Software (libx264) video transcode is much faster on weak ARM hosts, so playback keeps up with realtime: encode uses all CPU cores (`-threads`), and the scaler **never upscales** — the target box is capped to the source size via `min(W,iw)`/`min(H,ih)`, so a small source (e.g. 720x400) is encoded at its own resolution instead of being scaled up to the viewport (far fewer pixels).
|
|
4
12
|
- **New**: Adaptive software preset (preset auto-benchmark). At startup the proxy benchmarks libx264 presets (`fast`→`ultrafast`) on this host and records encode throughput (pixels/sec). Per stream, `hls-session-manager` picks the **highest-quality preset that still encodes the actual (source-capped) output resolution faster than realtime** with a safety margin, falling back to `ultrafast`. This maximises quality without dropping below 1× (which causes stalls). Logged as `video=libx264/<preset>` at session start.
|
package/package.json
CHANGED
package/routes/stream/get.js
CHANGED
|
@@ -64,6 +64,11 @@ export async function handleStreamGet(req, reply, { sourceRegistry, torrentPool
|
|
|
64
64
|
const releaseFile = torrentPool.acquireFile(torrent, fileIndex);
|
|
65
65
|
|
|
66
66
|
const range = parseRange(req.headers.range, file.length);
|
|
67
|
+
// Prioritize the pieces at this read position so a seek (a request at a new
|
|
68
|
+
// byte offset) downloads first instead of waiting behind the sequential
|
|
69
|
+
// backlog — this is what caused ~15-18 s stalls when seeking into an
|
|
70
|
+
// undownloaded region.
|
|
71
|
+
torrentPool.prioritizeByteRange(torrent, fileIndex, range ? range.start : 0);
|
|
67
72
|
reply.header("Accept-Ranges", "bytes");
|
|
68
73
|
reply.header("Content-Type", "application/octet-stream");
|
|
69
74
|
reply.header("Content-Disposition", `inline; filename*=UTF-8''${encodeURIComponent(file.name)}`);
|
package/services/hwaccel.js
CHANGED
|
@@ -37,8 +37,11 @@ const BENCHMARK_REF_W = 640;
|
|
|
37
37
|
const BENCHMARK_REF_H = 360;
|
|
38
38
|
const BENCHMARK_DURATION_SEC = 3;
|
|
39
39
|
// Require the encoder to be this much faster than realtime for the target
|
|
40
|
-
// resolution
|
|
41
|
-
|
|
40
|
+
// resolution. The benchmark runs at startup with an idle CPU; during playback
|
|
41
|
+
// ffmpeg competes with in-process WebTorrent (download + hashing) and delivery,
|
|
42
|
+
// so real throughput is lower. A generous margin keeps playback above 1× under
|
|
43
|
+
// that real load and absorbs complex scenes.
|
|
44
|
+
const PRESET_SPEED_MARGIN = 1.8;
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* @param {number} targetWidth
|
package/services/torrent-pool.js
CHANGED
|
@@ -10,6 +10,11 @@ import crypto from "node:crypto";
|
|
|
10
10
|
import WebTorrent from "webtorrent";
|
|
11
11
|
import { logger } from "../utils/logger.js";
|
|
12
12
|
|
|
13
|
+
// Bytes ahead of a read position to mark CRITICAL (download-first) on each
|
|
14
|
+
// range request. Big enough to unstick a seek into an undownloaded region,
|
|
15
|
+
// small enough not to make "everything critical" (which defeats prioritization).
|
|
16
|
+
const PRIORITY_WINDOW_BYTES = 8 * 1024 * 1024;
|
|
17
|
+
|
|
13
18
|
/**
|
|
14
19
|
* Decode a raw torrent source value into the format expected by WebTorrent.
|
|
15
20
|
*
|
|
@@ -318,4 +323,45 @@ export class TorrentPool {
|
|
|
318
323
|
}
|
|
319
324
|
}
|
|
320
325
|
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Mark the torrent pieces covering a byte window of a file as CRITICAL, so
|
|
329
|
+
* WebTorrent downloads them before the rest of the selected file. Called on
|
|
330
|
+
* every range request: after a seek, the new read position jumps the download
|
|
331
|
+
* queue instead of waiting behind the sequential backlog (which caused
|
|
332
|
+
* ~15-18 s stalls when seeking into an undownloaded region).
|
|
333
|
+
*
|
|
334
|
+
* @param {import("webtorrent").Torrent} torrent
|
|
335
|
+
* @param {number} fileIndex
|
|
336
|
+
* @param {number} byteStart - Start offset within the file.
|
|
337
|
+
* @param {number} [windowBytes] - Bytes ahead of `byteStart` to prioritize.
|
|
338
|
+
* @returns {void}
|
|
339
|
+
*/
|
|
340
|
+
prioritizeByteRange(torrent, fileIndex, byteStart, windowBytes = PRIORITY_WINDOW_BYTES) {
|
|
341
|
+
if (!torrent || typeof torrent.critical !== "function" || !Array.isArray(torrent.files)) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const pieceLength = Number(torrent.pieceLength);
|
|
345
|
+
if (!Number.isFinite(pieceLength) || pieceLength <= 0) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const file = torrent.files[fileIndex];
|
|
349
|
+
if (!file) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const fileOffset = Number.isFinite(file.offset) ? file.offset : 0;
|
|
353
|
+
const safeStart = Math.max(0, Number(byteStart) || 0);
|
|
354
|
+
const absStart = fileOffset + safeStart;
|
|
355
|
+
const absEnd = Math.min(fileOffset + file.length - 1, absStart + Math.max(1, windowBytes) - 1);
|
|
356
|
+
const startPiece = Math.floor(absStart / pieceLength);
|
|
357
|
+
const endPiece = Math.floor(absEnd / pieceLength);
|
|
358
|
+
if (endPiece < startPiece) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
torrent.critical(startPiece, endPiece);
|
|
363
|
+
} catch {
|
|
364
|
+
// Best effort — never break streaming because prioritization failed.
|
|
365
|
+
}
|
|
366
|
+
}
|
|
321
367
|
}
|